Динамическая форма angular2 с *ngFor, двусторонней привязкой [(ngModel)] и проверкой формы

Постановка задачи

Я пытаюсь создать динамическую форму, где части интерфейса реагируют на обновления модели:

  • пользователь нажимает на кнопку
  • новая сущность модели добавляется во внутренний список компонентов, и создается новая группа элементов управления (с несколькими дочерними элементами управления, каждый с выделенными валидаторами), которые присоединяются к форме.
  • каждый из этих дочерних элементов управления должен устанавливать двустороннюю привязку, соответствующую соответствующей записи в модели [(ngModel)], и распространять состояние до формы верхнего уровня

Где я застрял

Моя текущая проблема заключается в том, что я не знаю, как ссылаться на новую группу управления (хранится в ControlArray) из шаблона.

Все решения для динамических форм, которые я нашел, работали без модели поддержки и двусторонней привязки данных.

Код

Я создал 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 ответ

Решение
Другие вопросы по тегам