Вы правы, здесь проблема с ng-шаблоном.
Директива
FormControlName
в значительной степени опирается на иерархию элементов над текущим элементом для определения FormControl
.
Так как вы используете ngTemplateOutlet
, эта иерархия перепуталась, и Angular не может найти родительские FormGroup
или FormArray
элементы управления.
Вы можете реорганизовать свой пример для использования вложенных компонентов, и он должен работать, если вы сохраните иерархию дочерних элементов AbstractControls
.
С другой стороны, вы можете заставить его работать с ngTemplateOutlet
, если будете использовать директиву FormControl .
Вам нужно только убедиться, что вы правильно указали элемент 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, []);
}
}
Ng-run Пример