Динамическая форма angular2 с *ngFor, двусторонней привязкой [(ngModel)] и проверкой формы
Постановка задачи
Я пытаюсь создать динамическую форму, где части интерфейса реагируют на обновления модели:
- пользователь нажимает на кнопку
- новая сущность модели добавляется во внутренний список компонентов, и создается новая группа элементов управления (с несколькими дочерними элементами управления, каждый с выделенными валидаторами), которые присоединяются к форме.
- каждый из этих дочерних элементов управления должен устанавливать двустороннюю привязку, соответствующую соответствующей записи в модели [(ngModel)], и распространять состояние до формы верхнего уровня
Где я застрял
Моя текущая проблема заключается в том, что я не знаю, как ссылаться на новую группу управления (хранится в ControlArray) из шаблона.
Все решения для динамических форм, которые я нашел, работали без модели поддержки и двусторонней привязки данных.
- Angular2 формируют ControlGroup, которые содержат неопределенное число Control (и упомянутый plunkr)
- ngControl с ngFor в Angular2
Код
Я создал plunkr для кода ниже: https://plnkr.co/edit/nP6hcIXKA0jz2F8Epg2L?p=preview
Моя (простая) модель данных:
class Entry {
constructor(
public _id: string,
public _title: string
) {}
}
class Data {
constructor(
public heading: string,
public entries: Entry[] = []
) {}
}
Мой шаблон:
@Component({
selector: 'my-app',
template: `
<h1>Dynamic Form</h1>
<form [ngFormModel]="formModel">
<label>heading: </label>
<input type="text" [(ngModel)]="heading" [ngFormControl]="formModel.controls.heading">
<div>
<hr>
<!-- PROBLEM: how to get a reference to a single Control from within ControlGroup from within formModel.controls['entries'] that can be wired with belows <input> fields? -->
<div *ngFor="#entry of data.entries; #i = index">
<label>id: </label>
<input type="text" [(ngModel)]="entry._id" #ctrlId="ngForm"> <span><b>is valid: </b>{{ctrlId.control.valid}}</span>
<br>
<label>title: </label>
<input type="text" [(ngModel)]="entry._title" #ctrlTitle="ngForm"><span><b>is valid: </b>{{ctrlTitle.control.valid}}</span>
<hr>
</div>
</div>
</form>
<input type="button" (click)="add()" value="add new entry">
<div>{{ctrlCount}}</div>
<div>{{debug}}</div>
<div>{{debugForm}}</div>
`
})
Мой компонент:
export class DynamicForm implements OnInit {
data:Data;
formModel:ControlGroup;
constructor(private fb:FormBuilder) {
this.formModel = fb.group({
heading: fb.control('test heading', Validators.required),
entries: fb.array([])
})
}
ngOnInit():void {
/* init the heading */
this.data = new Data('test heading');
/* init the entries > add to model, create control and add it to ControlArray */
[
new Entry('1', 'one'),
new Entry('2', 'two'),
new Entry('3', 'three'),
new Entry('4', 'four'),
].forEach((e:Entry) => this.add(e));
}
add(e:Entry):void {
let id:string = e ? e._id : '';
let title:string = e ? e._title : '';
(<ControlArray>this.formModel.controls['entries']).push(this.fb.group({
id: this.fb.control(id, Validators.required),
title: this.fb.control(title, Validators.required),
}));
this.data.entries.push(new Entry(id, title));
}
get debug() {
return JSON.stringify({debug_data: this.data});
}
get debugForm() {
return JSON.stringify({
debug_form: {
dirty: this.formModel.dirty,
pristine: this.formModel.pristine,
touched: this.formModel.touched,
untouched: this.formModel.untouched,
valid: this.formModel.valid,
}
});
}
get ctrlCount() {
return (<ControlArray>this.formModel.controls['entries']).length;
}
}
1 ответ
Решение
Описание ControlArray.at()
: https://angular.io/docs/ts/latest/api/common/index/ControlArray-class.html