英文 | https://medium.com/javascript-in-plain-english/how-to-create-a-complex-dynamic-form-in-angular-19a472af6060
翻譯 | web前端開發(ID:web_qdkf)
表單是Web應用程序中非常重要的部分。有時我們會遇到一個情況,即form元素是靜態的並且定義明確。有時,Web應用程序開發會從靜態形式擴展到動態形式。動態表單沒有靜態字段。
動態表單和控件可以基於某些事件生成。在本文中,我們將基於單擊加號生成控件。單擊減號按鈕將其刪除。
這是一種類似於芯片的結構,其中我們具有加號和減號按鈕來添加和刪除文本字段。在本文中,我們以產品爲例。
角度設計
Angular支持兩種動態控制設計。
模板驅動-在模板驅動的表單中,可以更好地控制角度模板。該模板非常強大,並且具有Angular框架內置的大多數功能。我們只需要手工設計出乾淨整潔的HTML。我自由也正在使用這種方法。
反應形式-同樣非常強大,大多數控件位於組件中。需要Formbuilder來構建表單。
構建動態組件
我們正在構建產品組件。該產品具有產品名稱和產品代碼。該產品還有另一個可選參數,即產品功能。產品功能部件包含功能部件名稱,功能部件添加日期和功能部件說明。
這些功能可以隨時添加。我們要添加的功能數量沒有限制。如下所示:
模型設計
創建界面產品功能並將其傳遞到產品界面。產品界面採用了諸如數組之類的產品功能。下面是數據模型。
export interface productFeatures{
featureName:string,
featureAddedDate:string,
featureDescription?:string
}
export interface Product<productFeatures> {
id: number | null;
productName: string;
productCode: string;
description?: string;
productFeatures?: productFeatures[];
}
export const products: Product<productFeatures>[] = [
{
id: 1,
productName: 'Netgear Cable Modem',
productCode: 'CM700',
description: 'Netgear Cable Modem compatible with all cables',
productFeatures: [{
featureName:"",
featureAddedDate:"",
featureDescription:""
}]
},
];
接着就是創建一個子組件。是的,我在這裏創建一個子組件。因爲在大多數企業應用程序中,我們應該創建一小組組件。
<app-product-feature> </ app-product-feature>
我們必須將數據從子組件傳遞到父組件。因此,這裏定義了parentProductFields函數,該函數將數據設置爲父組件。
<app-product-feature
(parentProductFields)=“ parentProductFields($ event)”>
</ app-product-feature>
在父組件中創建方法parentProduct。此方法將設置this.productField,其類型爲Product <productFeatures>。
productField: Product<productFeatures> = {} as Product<productFeatures>;parentProductFields(value) {console.log(`calling hour recieved from parent is ${JSON.stringify(value)}`)this.productField = value;}
現在,在目錄product下創建子組件產品功能。將父產品字段定義爲@Output屬性。parentProductFields的類型爲Product <productFeatures>。
@Output() parentProductFields = new
EventEmitter<Product<productFeatures>>()
用初始數據初始化。在這裏,產品ID初始化爲1,其餘字段初始化爲空。每當添加新功能時,產品功能數組都會包含一個對象,我們將在功能數組中推送一個新功能對象。
productField: Product<productFeatures> = {
id: 1,
productName: ‘’,
productCode: ‘’,
description: ‘’,
productFeatures: [{
featureName:””,
featureAddedDate:””,
featureDescription:””
}]
}
完整的產品組件將如下所示。
import { Component, Input, Output,EventEmitter } from '@angular/core';
import {Product,productFeatures} from './product';
@Component({
selector: 'app-product-feature',
templateUrl: "./product-feature.component.html"
})
export class ProductFeatureComponent {
@Input() childProductField: Product<productFeatures>;
@Output() parentProductFields = new EventEmitter<Product<productFeatures>>();
productField: Product<productFeatures> = {
id: 1,
productName: '',
productCode: '',
description: '',
productFeatures: [{
featureName:"",
featureAddedDate:"",
featureDescription:""
}]
}
addNewProdField(index: number): void {
let prod: productFeatures = {
"featureName": "",
"featureAddedDate": "",
"featureDescription": ""
} ;
this.productField.productFeatures.push(prod);
console.log(`In method addNewProdField field index is ${index} and field is ${JSON.stringify(JSON.stringify( this.productField))}`);
this.parentProductFields.emit(this.productField);
}
removeNewProdField(index: number): void {
this.productField.productFeatures.splice(index, 1);
console.log(`In method addNewProdField field index is ${index}`);
this.parentProductFields.emit(this.productField);
}
}
現在開始寫HTML結構。在HTML產品名稱中,產品代碼爲文本框。產品功能是我們迭代以生成動態組件的動態表。
<tbody><ng-template ngFor let-prod [ngForOf]=”productField.productFeatures” let-i=”index”><tr><td><!-- Product feature here --><td></tr></ng-template></tbody>
在開發此控件時,發現了一件重要的事情。你必須爲每個控件動態創建ID。否則,將創建該控件的相同深層副本。
<td><input class=”form-control minimal” id=”{{featureAddedDate + prod}}”[(ngModel)]=”prod.featureAddedDate”></td>
完整的HTML文件如下所示。
<h5>Product component</h5><div class="row"> <div class="col-md-12"> </div></div><div class="row"> <div class="col-md-12"> </div></div><div class="row required-field"> <div class="col-md-4"> <label class="cus-form-label">Product Name</label> <input class="form-control minimal" id="productName" [(ngModel)]="productField.productName"> </div></div><div class="row"> <div class="col-md-12"> </div></div><div class="row required-field"> <div class="col-md-4"> <label class="cus-form-label">Product Code</label> <input class="form-control minimal" id="productCode" [(ngModel)]="productField.productCode"> </div></div><div class="row"> <div class="col-md-12"> </div></div><label class="cus-form-label">Product Features</label><div class="row"> <div class="col-md-12"> </div></div><table class="col-lg-8" id="callingHours-id" style="background-color: white;"> <thead> <tr> <th class="bordered-cell" style="width: 30%;">Feature Name</th> <th class="bordered-cell">Feature Date</th> <th class="bordered-cell">Feature Description</th> </tr> </thead> <tbody> <ng-template ngFor let-prod [ngForOf]="productField.productFeatures" let-i="index"> <tr> <td> <input class="form-control minimal" id="{{name + prod}}" [(ngModel)]="prod.featureName"> </td> <td> <input class="form-control minimal" id="{{featureAddedDate + prod}}" [(ngModel)]="prod.featureAddedDate"> </td> <td> <input class="form-control minimal" id="{{featureDescription + prod}}" [(ngModel)]="prod.featureDescription"> </td> <td> <ng-container> <button id="{{'remoeMinus' +i}}" class="btn btn-xs btn-danger" (click)="removeNewProdField(i)"> <i class="fa fa-minus"></i> </button> </ng-container> <ng-container> <button id="{{'addrec'+ i }}" class="btn btn-xs btn-primary" (click)="addNewProdField(i)"> <i class="fa fa-plus"></i> </button> </ng-container> </td> </tr> </ng-template> </tbody></table>
添加產品功能
要添加新產品功能,請添加以下代碼。此方法向this.productField.productFeatures添加新功能產品
addNewProdField(index: number): void {let prod: productFeatures = {“featureName”: “”,“featureAddedDate”: “”,“featureDescription”: “”} ;this.productField.productFeatures.push(prod);console.log(`In method addNewProdField field index is ${index} and field is ${JSON.stringify(JSON.stringify( this.productField))}`);this.parentProductFields.emit(this.productField);}
刪除產品功能
要刪除產品功能,請從this.productField.productFeatures中拼接產品索引。
removeNewProdField(index: number): void {this.productField.productFeatures.splice(index, 1);console.log(`In method addNewProdField field index is ${index}`);this.parentProductFields.emit(this.productField);}
要顯示子組件數據中的更改,請在父組件HTML中添加以下行。
<pre>{{productField | json}}</ pre>
現在,你可以看到添加的字段的JSON數據。
{ "id": 1, "productName": "Netgear Wireless Router", "productCode": "NG123", "description": "", "productFeatures": [ { "featureName": "capacity", "featureAddedDate": "05/01/2020", "featureDescription": "123" }, { "featureName": "range", "featureAddedDate": "05/01/2020", "featureDescription": "123" } ]}
總結
同樣,我們可以將示例擴展爲多個產品。創建產品數組並使用* ngFor循環進行顯示。在這種情況下,我們應該有兩個索引。
產品的一個索引和其他產品功能。產品索引將有助於添加到產品陣列中,而功能索引將有助於添加/刪除產品功能。
在stackblitz項目中附加完整的代碼示例,截圖如下:
演示地址:https://stackblitz.com/edit/angular-dynamic-component-1-h6hxos