Техника по построению сложных форм из вложенных компонентов в Angular

Feb 17, 2019

В этой статье мы покажем, как реализовать формы из вложенных компонентов с реактивным АПИ ангуляра.

С реактивным подходом мы создаем модель формы и передаем эту модель (или ее часть) из одного компонента другому через входящие свойства.

Создадим реактивную форму из нескольких вложенных компонентов

Основной компонент формы NestedFormComponent

Давайте рассмотрим основной компонент формы NestedFormComponent и то, каким образом мы реализуем в нем будущие компоненты формы. Особенно стоит обратить внимание как мы создаем модель формы в верхнем компоненте NestedFormComponent. Модель формы будем пробрасывать в дочерние компоненты.

<form [formGroup]="myForm" (ngSubmit)="submit()">
    <h4>Form</h4>
    <hr>
    <app-items-array
            formArrayName="items"
            [itemsFormArray]="myForm.get('items')">
    </app-items-array>
    <hr>
    <div class="form-group">
        <input type="submit" class="form-control" value="Submit" [disabled]="myForm?.invalid">
    </div>
</form>

<h6 class="mt-3">FormGroup Model (<code>myForm.value</code>)</h6>
<div>
    <pre><code>{{ myForm?.value | json }}</code></pre>
</div>
@Component({
    selector: 'app-nested-form',
    templateUrl: './nested-form.component.html',
    styleUrls: ['./nested-form.component.css']
})
export class NestedFormComponent implements OnInit {

    myForm: FormGroup;

    constructor(
        private fb: FormBuilder
    ) {}

    ngOnInit() {
        // build the form model
        this.myForm = this.fb.group({
            items: new FormArray([
                    new FormGroup({
                        name: new FormControl('aaa1', Validators.required),
                        quantity: new FormControl(100)
                    }),
                    new FormGroup({
                        name: new FormControl('', Validators.required),
                        quantity: new FormControl(100)
                    })
                ],
                ItemsValidators.minQuantitySum(300))
        })
    }

    submit() {
        console.log("Reactive Form submitted: ", this.myForm)
    }
}

Расширим форму компонентом ItemFormControlComponent

Извлечем итем из FormArray в отдельный компонент. Назовем его ItemFormControlComponent. Передадим индекс и экземпляр FormGroup как входящие свойства. FormGroup создается функцией addItem() в компоненте ItemsFormArrayComponent.

Также будем эмитить наверх индекс FormGroup, чтобы иметь возможность удалять компонент в родительской форме ((removed)="itemsFormArray.removeAt($event)").

<div class="form-group row" [formGroup]="item">
    <div class="col-sm-6">
        <label [attr.for]="'name'+index">Name</label>
        <input type="text" class="form-control"
               [attr.id]="'name'+index" formControlName="name">
    </div>
    <div class="col-sm-5">
        <label [attr.for]="'quantity'+index">Quantity</label>
        <input type="number" class="form-control"
               [attr.id]="'quantity'+index" formControlName="quantity">
    </div>
    <div class="col-sm-1 py-1">
        <button type="button" class="btn"
                (click)="removed.emit(index)">-</button>
    </div>
</div>
@Component({
    selector: 'app-item-control',
    templateUrl: './item-control.component.html',
    styleUrls: ['./item-control.component.css']
})
export class ItemControlComponent {
    @Input()
    public index: number;

    @Input()
    public item: FormGroup;

    @Output()
    public removed: EventEmitter<number> = new EventEmitter<number>();
}

Расширим форму компонентом ItemsFormArrayComponent

Второй шаг - извлечь массив итемов в свой собственный компонент (назовем его ItemsFormArrayComponent). Передадим FormArrayas как входящее свойство. Отметьте небольшое изменение по сравнению с предыдущим подходом: мы запишем formArrayName="items" на рутовом компоненте:

    <items-array formArrayName="items" [items]="myForm.get('items')">

Конечно, мы переиспользуем ItemFormControlComponent из предыдущего шага.

<fieldset>
    <h6>Items</h6>
    <div *ngIf="itemsFormArray.hasError('minSum')">
        Вы должны купить по крайней мере {{ itemsFormArray.getError('minSum') }}.
    </div>
    <app-item-control
            *ngFor="let item of itemsFormArray.controls; let i=index"
            [index]="i"
            [item]="item"
            (removed)="itemsFormArray.removeAt($event)">
    </app-item-control>
</fieldset>
<button type="button" class="btn btn-link"
    (click)="addItem()">Add another item</button>
@Component({
    selector: 'app-items-array',
    templateUrl: './items-array.component.html',
    styleUrls: ['./items-array.component.css']
})
export class ItemsArrayComponent {

    @Input()
    public itemsFormArray: FormArray;

    addItem() {
        this.itemsFormArray.push(new FormGroup({
            name: new FormControl('aaa', Validators.required),
            quantity: new FormControl(100)
        }))
    }
}

Три компонента и директивы

Давайте провиализируем. Красными выделены компоненты, белым директивы.

Как вы видите, экземпляры модели формы передаются от верхнего компонента далее вниз по дереву.

Резюме

Модель формы сконструирована при помощи FormControl, FormArray, FormGroup. Директивы соединяют существующую модель формы внутри шаблона (реактивные формы).

Модель формы можеь быть проброшена из одного компанента в другой через входящие свойтсва. Эта техника позволяет создавать довольно утонченные и сложные формы, которые состоят из нескольких компонентов. Реактивные формы хорошо подходят для этой идеи, позволяя относительно лекго создавать вложенные формы.

Источник Создаем вложенные, реактивные формы

Добавить комментарий
Комментарии:
IT-образование
Apr 17, 2020
h5h65h65h56h