Angular 動態表單(根據選擇聯動顯示)
首先
記錄一下剛剛完成的一個功能
需求是根據固定的層級結構做動態聯動,在網上找了很多動態表單的相關教程,大部分都是在最開始加載就生成表單.而我的需求是需要在選擇了父級之后再展示父級下的選項
實現
-
准備階段
配置好已知的層級結構
const itemConfig = [{ 'key': 'tr069', 'type': 'parent',// parent =>含有子級 item=>最底層級 'item_type': 'select',// 本層級的類型 parent 默認為select 'children': [ { 'key': 'url', 'type': 'item', 'item_type': 'input', 'value': '' }, { 'key': 'username', 'type': 'item', 'item_type': 'input', 'value': '' }, { 'key': 'password', 'type': 'item', 'item_type': 'input', 'value': '' }, { 'key': 'data', 'type': 'item', 'item_type': 'object', 'value': { 'key': '', 'value': '' } } ] },{ 'key': 'gateway', 'type': 'parent', 'item_type': 'select', 'children': [ { 'key': 'gw', 'type': 'item', 'item_type': 'input', 'value': '' } ] },{ 'key': 'optimy', 'type': 'parent', 'item_type': 'select', 'children': [ { 'key': 'enable', 'type': 'item', 'item_type': 'select', 'value': [true, false] } ] }, ];
頁面上創建初始層級
在初始表單選項上綁定change事件
<form autocomplete="off" [formGroup]="itemForm" fxLayout="column"> <div fxLayout="row" fxLayoutAlign="center center"> <mat-label>Group:</mat-label> <mat-form-field appearance="outline"> <mat-select formControlName="group" (selectionChange)="changeSelect($event.value,'group')"> <mat-option *ngFor="let item of itemConfig" [value]="item"> {{item.key}} </mat-option> </mat-select> </mat-form-field> </div> </form>
頁面配置表單的初始選項
itemForm: FormGroup; // 根據父級的選擇 展示的item列表 itemPushDom = []; ngOnInit(): void { this.itemForm = this.createFrom(); } createFrom(): any { const group = this._formBuilder.group({ 'group': this._formBuilder.control('') }); return group; }
-
實現動態插入
通過之前的操作 現在我們的頁面上已經能看到初始的選項了
接下來就是實現動態插入 , 其實簡單來說就是分為兩個步驟: Dom插入可選項 和 FormGroup添加項
再加上change父級之后清除之前的選擇
Dom插入可選項
需要配合之前初始選項綁定的change事件 傳遞children的信息
html 上也需要配置好相關的可能出現的類型 通過ngSwtech 展示 (本例類型較少,只有select和input 如果類型較多可以參考官方文檔的動態表單 通過component控制出現的類型)
FormGroup添加項
通過 addControl()/removeControl() 對FormGroup 表單項 進行添加和刪除(也可以添加驗證)
下面直接上代碼
component.html (放在上一段form里)
<div fxLayout="row" fxLayoutAlign="center center" *ngFor="let newItem of itemPushDom"> <mat-label >{{newItem.name}}:</mat-label> <mat-form-field appearance="outline" [ngSwitch]="newItem.type"> <mat-select *ngSwitchCase="'select'" formControlName="{{newItem.name}}" (selectionChange)="changeSelect($event.value,newItem.name)" required> <mat-option *ngFor="let options of newItem.options" [value]="options.value"> {{options.key}} </mat-option> </mat-select> <input matInput *ngSwitchCase="'input'" formControlName="{{newItem.name}}" [value]="newItem.value" required> </mat-form-field> </div>
component.ts
changeSelect(item: any, itemName: string): void { // changeSelect 后檢查是否需要刪除原選項關聯的item // 如果change的是父級的選項 遞歸刪除已選 this.checkItemDom(itemName); this.createItemDom(item, itemName); } createItemDom(item: any, itemName: string): void { // 動態添加FromControl this.addFromControl(item.key); const options = []; // 判斷選擇的item是否還有子級 (select) if (item.type === 'parent') { // 默認parent都是select // 查找子項點展示 item.children.forEach(eleParent => { options.push({ key: eleParent.key, value: eleParent }); }); this.addFromDom(item.key, itemName, item.item_type, '', options, item.type); } else if (item.type === 'item') { if (item.item_type === 'select') { item.value.forEach(eleItem => { options.push({ key: eleItem, value: eleItem }); }); this.addFromDom(item.key, itemName, item.item_type, item.value, options, item.type); } else { // 這里可以擴展多種類型 this.addFromDom(item.key, itemName, item.item_type, item.value, options, item.type); } } } // add/remove itemPushDom // 保存item的name/type/parent 在二維數組中保存子父級關聯 addFromDom(name: string, key: string, type: string, value: string, options: any, item_type: string): void { this.itemPushDom.push({ parent: key, name: name, type: type, item: item_type, value: value, options: options }); } checkItemDom(itemName: string): any { for (let i = 0; i < this.itemPushDom.length; i++) { if (this.itemPushDom[i].parent === itemName) { // 根據子父級關系 父級改變 查找相對應的子級刪除 Dom和FormGroup this.checkItemDom(this.itemPushDom[i].name); this.delFromControl(this.itemPushDom[i].name); this.itemPushDom.splice(i, 1); } } } addFromControl(name: string): void { this.itemForm.addControl(name, this._formBuilder.control('')); } delFromControl(name: string): void { this.itemForm.removeControl(name); }
最后只需要 通過 this.itemForm.value 就可以獲得所選的值
或者保存itemPushDom