我正在使用angular 7应用程序,并且正在编写一个通用的动态表单创建器。我正在使用反应式表单和ng-template and <ng-container *ngTemplateOutlet>
来递归地显示表单元素。可以看到项目代码Here。
但是我面临以下错误
ERROR Error: Cannot find control with name: 'data'
at _throwError (forms.js:1775)
at setUpControl (forms.js:1683)
at FormGroupDirective.push../node_modules/@angular/forms/fesm5/forms.js.FormGroupDirective.addControl (forms.js:4532)
at FormControlName.push../node_modules/@angular/forms/fesm5/forms.js.FormControlName._setUpControl (forms.js:5030)
at FormControlName.push../node_modules/@angular/forms/fesm5/forms.js.FormControlName.ngOnChanges (forms.js:4980)
at checkAndUpdateDirectiveInline (core.js:9239)
at checkAndUpdateNodeInline (core.js:10507)
at checkAndUpdateNode (core.js:10469)
at debugCheckAndUpdateNode (core.js:11102)
at debugCheckDirectivesFn (core.js:11062)
但是,如果我看到我的formGroup对象,则可以看到一个控件,其中数据是控件的名称,如下所示。
我在做什么错?请帮忙。
问题的重制是Here stackblitz
答案 0 :(得分:4)
您是对的,这里的ng-template存在问题。
FormControlName
指令在很大程度上取决于当前元素上方的元素层次结构来确定FormControl
。
由于您使用的是ngTemplateOutlet
,因此该层次结构混杂在一起,并且Angular找不到父FormGroup
或FormArray
控件。
您可以重构示例以使用嵌套组件,并且如果保留孩子AbstractControls
的层次结构,它应该可以工作。
另一方面,如果要使用FormControl指令,则可以使其与ngTemplateOutlet
一起使用。
您只需要确保为input
元素提供了正确的控制即可。
这是完成的方法:
<form [formGroup]="form">
<ng-container *ngTemplateOutlet="controlList; context: {controls: schema, path: []}"></ng-container>
<ng-template #controlList let-controls="controls" let-path="path" let-isArray="isArray">
<ng-container *ngFor="let control of controls; let i = index;">
<ng-container *ngIf="control?.type === 'simple'">
<div class="control">
<label>
{{ control.label }}
<input type="text" [formControl]="form.get(isArray ? path.concat(i, control.name) : path.concat(control.name))">
</label>
</div>
</ng-container>
<ng-container *ngIf="['object', 'array'].indexOf(control.type) > -1">
<fieldset>
<legend>{{control.name}}</legend>
<ng-container
*ngTemplateOutlet="controlList; context: {controls: control.items, isArray: control.type === 'array', path: isArray ? path.concat(i, control.name) : path.concat(control.name)}">
</ng-container>
</fieldset>
</ng-container>
</ng-container>
</ng-template>
</form>
这里的关键是要正确到达目标控件,path
是我作为上下文的一部分传递给下一个子级别的。
此外,如果要生成无限结构嵌套元素,则应重构代码以构建FormGroup:
ngOnInit() {
this.schema = data.start.fields.schema;
this.form = this.createFormGroup(this.schema);
}
createFormGroup(items) {
let group: { [controlId: string]: AbstractControl; } = {};
items.forEach((item) => group[item.name] = this.createFormControl(item));
return this.fb.group(group);
}
createFormControl(item) {
if (item.type === 'array') {
return this.fb.array(item.items.map((subItem) => {
return this.fb.group({
[subItem.name]: this.createFormControl(subItem)
})
}));
} else if (item.type === 'object') {
return this.createFormGroup(item.items);
} else {
return this.fb.control(item.value, []);
}
}
答案 1 :(得分:0)
问题在于formcontrol无法确定父级,这就是为什么出现控制台错误,有时输入的值也未正确映射的原因,因此适用的解决方案是
<form [formGroup]="control">
</form>
在具有[formGroupName] =“ index”
的div内所以看起来像
<div *ngFor="let control of recursiveForm.get('controlList')['controls']; let index = index" [formGroupName]= "index">
<form [formGroup]="control">
<input type="text" formControlName="name">
</form>
</div>