Angular嵌套响应式表单中ngTemplateOutlet使用问题求助
基于JSON自动生成Angular响应式表单的模板上下文问题
我正尝试基于JSON数据生成Angular响应式表单,已通过TypeScript代码生成对应结构的FormGroup,手动编写的HTML表单可正常与FormGroup同步绑定,但使用ng-template和ngTemplateOutlet自动生成HTML表单时,出现"Cannot find control with name"错误,推测是模板未获取到表单上下文,求助如何在模板上下文中传递表单实例以实现表单同步。
用于生成表单的JSON数据
{ "user":{ "name": "string", "mail":"string", "hobbies":{ "sport":"string", "musique":"string", "games":"string" } }, "house":{ "adress":"string", "size":"string", "rooms":{ "kitchen":"string", "bedrooms":{ "bedroom1":"string", "bedroom2":"string" } } } }
生成FormGroup的TypeScript代码
objectform(data:any): FormGroup{ let newForm=this.fb.group({}) for (let item of Object.keys(data)){ if (data[item]=="string"){ newForm.addControl(item, this.fb.control('',[])) } else { newForm.addControl(item,this.objectform(data[item])); } } return newForm; } ngOnInit():void { this.testForm = this.fb.group({}); for (let item of Object.keys(this.data)){ if (this.data[item]!="string"){ this.testForm.addControl(item,this.objectform(this.data[item])); } else{ this.testForm.addControl(item, this.fb.control('',[])) } } console.log("testForm: ",this.testForm) }
可正常工作的手动编写HTML代码
<div> <form [formGroup]="testForm"> <div formGroupName="user"> <h1>User Infos:</h1> <mat-form-field appearance="fill" > <label>name :</label> <textarea matInput formControlName="name"></textarea> </mat-form-field> <mat-form-field appearance="fill" > <label>mail :</label> <textarea matInput formControlName="mail"></textarea> </mat-form-field> <div formGroupName="hobbies"> <h2>Hobbies:</h2> <mat-form-field appearance="fill" > <label>sport :</label> <textarea matInput formControlName="sport"></textarea> </mat-form-field> <mat-form-field appearance="fill" > <label>musique :</label> <textarea matInput formControlName="musique"></textarea> </mat-form-field> <mat-form-field appearance="fill" > <label>games :</label> <textarea matInput formControlName="games"></textarea> </mat-form-field> </div> </div> <div formGroupName="house"> <h1>House Infos:</h1> <mat-form-field appearance="fill" > <label>adress :</label> <textarea matInput formControlName="adress"></textarea> </mat-form-field> <mat-form-field appearance="fill" > <label>size :</label> <textarea matInput formControlName="size"></textarea> </mat-form-field> <div formGroupName="rooms"> <h2>Rooms:</h2> <mat-form-field appearance="fill" > <label>kitchen :</label> <textarea matInput formControlName="kitchen"></textarea> </mat-form-field> <div formGroupName="bedrooms"> <h2>bedrooms:</h2> <mat-form-field appearance="fill" > <label>bedroom1 :</label> <textarea matInput formControlName="bedroom1"></textarea> </mat-form-field> <mat-form-field appearance="fill" > <label>bedroom2 :</label> <textarea matInput formControlName="bedroom2"></textarea> </mat-form-field> </div> </div> </div> </form> <button mat-raised-button (click)="onSubmit()">Submit</button> </div>
无法正常工作的模板化HTML代码
<div> <form [formGroup]="testForm"> <div [formGroup]="testForm"> <ng-container *ngFor="let itemname of getKeys(data)"> <h1>{{itemname}} :</h1> <ng-container *ngIf="data[itemname] as item"> <ng-container *ngIf="item == 'string'"> <ng-container *ngTemplateOutlet="stringparameter; context: {parametername :itemname}"></ng-container> </ng-container> <ng-container *ngIf="item != 'string'" style="padding: 50px"> <div formGroupName="{{itemname}}"> <ng-container *ngTemplateOutlet="objectparameter; context: {parameter :item}"></ng-container> </div> </ng-container> </ng-container> </ng-container> </div> <ng-template #stringparameter let-parametername="parametername"> <mat-form-field appearance="fill" > <label>{{parametername}}</label> <textarea matInput formControlName="{{parametername}}"></textarea> </mat-form-field> </ng-template> <ng-template #objectparameter let-parameter="parameter"> <ng-container *ngFor="let itemname of getKeys(parameter)"> <h2>{{itemname}} :</h2> <ng-container *ngIf="parameter[itemname] as item"> <ng-container *ngIf="item == 'string'"> <ng-container *ngTemplateOutlet="stringparameter; context: {parametername :itemname}"></ng-container> </ng-container> <ng-container *ngIf="item != 'string'" style="padding: 50px"> <div formGroupName="{{itemname}}"> <ng-container *ngTemplateOutlet="objectparameter; context: {parameter :item}"></ng-container> </div> </ng-container> </ng-container> </ng-container> </ng-template> </form> <button mat-raised-button (click)="onSubmit()">Submit</button> </div>
错误信息
Error: Cannot find control with name: 'name' Error: Cannot find control with name: 'mail' Error: Cannot find control with name: 'hobbies' Error: Cannot find control with name: 'sport' Error: Cannot find control with name: 'musique' etc.
解决方案
问题核心是模板嵌套时未传递当前层级的FormGroup上下文,Angular响应式表单依赖组件树中的表单上下文定位控件,模板渲染无法自动继承父级formGroupName对应的FormGroup,需手动将当前层级的FormGroup传入模板上下文。
步骤1:修改模板上下文,传递当前FormGroup
在ngTemplateOutlet调用时,除了数据结构,额外传递当前对应的FormGroup实例:
根层级模板调用修改
<div [formGroup]="testForm"> <ng-container *ngFor="let itemname of getKeys(data)"> <h1>{{itemname}} :</h1> <ng-container *ngIf="data[itemname] as item"> <ng-container *ngIf="item == 'string'"> <ng-container *ngTemplateOutlet="stringparameter; context: {parametername: itemname, formGroup: testForm}"></ng-container> </ng-container> <ng-container *ngIf="item != 'string'" style="padding: 50px"> <div [formGroup]="testForm.get(itemname)"> <ng-container *ngTemplateOutlet="objectparameter; context: {parameter: item, formGroup: testForm.get(itemname)}"></ng-container> </div> </ng-container> </ng-container> </ng-container> </div>
objectparameter模板修改
递归调用时传递当前子项对应的FormGroup:
<ng-template #objectparameter let-parameter="parameter" let-formGroup="formGroup"> <ng-container *ngFor="let itemname of getKeys(parameter)"> <h2>{{itemname}} :</h2> <ng-container *ngIf="parameter[itemname] as item"> <ng-container *ngIf="item == 'string'"> <ng-container *ngTemplateOutlet="stringparameter; context: {parametername: itemname, formGroup: formGroup}"></ng-container> </ng-container> <ng-container *ngIf="item != 'string'" style="padding: 50px"> <div [formGroup]="formGroup.get(itemname)"> <ng-container *ngTemplateOutlet="objectparameter; context: {parameter: item, formGroup: formGroup.get(itemname)}"></ng-container> </div> </ng-container> </ng-container> </ng-container> </ng-template>
stringparameter模板修改
绑定到传入的FormGroup实例,确保控件找到正确上下文:
<ng-template #stringparameter let-parametername="parametername" let-formGroup="formGroup"> <mat-form-field appearance="fill" > <label>{{parametername}}</label> <textarea matInput [formControl]="formGroup.get(parametername)"></textarea> </mat-form-field> </ng-template>
步骤2:确保组件有getKeys方法
在组件类中添加遍历对象键的方法:
getKeys(obj: any): string[] { return Object.keys(obj); }
原理说明
- 原模板仅设置
formGroupName,但模板渲染时无法自动关联到对应FormGroup实例,导致控件找不到父级上下文。 - 手动传递当前层级的FormGroup实例后,控件可通过
[formControl]直接绑定到对应实例的控件,避免上下文丢失。 - 嵌套递归时,每层都传递当前子项的FormGroup,确保所有层级的控件都能找到正确的父级表单组。
内容的提问来源于stack exchange,提问作者agarnier22
相关产品推荐
相关产品推荐

