Как использовать реактивные формы в ngFor

Массив пользователей может иметь несколько элементов (пользователей). В шаблоне я перебираю этот массив и добавляю форму для каждого элемента. Сейчас каждая форма привязывается к одним и тем же значениям группы форм. Если я заполню форму для первого пользователя, кнопки отправки всех форм станут активными. Как указать каждой форме на уникальную formGroup или на уникальные переменные?

составная часть:

import { FormControl, FormGroup, Validators, FormBuilder} from '@angular/forms';
...
export class UserComponent implements OnInit {
 form: FormGroup;
 @input users: any[];

 constructor(private _fb: FormBuilder) {}
 ngOnInit() {
  this.form = this._fb.group({
   address: new FormControl('', [Validators.required]),
   phone: new FormControl('', [Validators.required]),
   });
 }

 add() {
   if (this.form.valid){
     // code
   }
 }

Шаблон:

 <div *ngFor="let user if users">
  {{user.name}}
    <form [formGroup]="form"  (ngSubmit)="add()">         
     <div class="form-group">
       <label>Address:</label>
       <input formControlName="address" class="form-control">
       <label>Phone:</label>
       <input formControlName="phone" class="form-control"></input>
     </div>
     <button type="submit" class="submit" [disabled]="!form.valid">Submit</button>    
    </form>
 </div>

person Anna Olshevskaia    schedule 27.07.2018    source источник


Ответы (3)


Чтобы решить вашу проблему, вы должны использовать formArray следующим образом:

В компоненте:

export class UserComponent implements OnInit {
    usersForm: FormGroup;
    errorMessage : string;

    constructor(private formBuilder: FormBuilder) {}

    ngOnInit() {
        this.usersForm= this.formBuilder.group({
            users: this.formBuilder.array([
                this.formBuilder.group({
                    address: [null, [Validators.required]],
                    phone: [null, [Validators.required]]
                })
            ])
        });
    }

    initUserRow(): FormGroup {
        return this.formBuilder.group({
            address: [null, [Validators.required]],
            phone: [null, [Validators.required]],
        });
    }

    addUserRow(): void {
        const usersArray=
            <FormArray>this.usersForm.controls['users'];
        usersArray.push(this.initUserRow());
    }

    removeUserRow(rowIndex: number): void {
        const usersArray= <FormArray>this.usersForm.controls['users'];
        if (usersArray.length > 1) {
            usersArray.removeAt(rowIndex);
        } else {
            this.errorMessage = 'You cannot delete this row! form should contain at least one row!';
            setTimeout(() => {
                this.errorMessage = null;
            }, 4000);
        }
    }

}

В представлении:

<form *ngIf="usersForm" [formGroup]="usersForm" (ngSubmit)="createUsers()">
    <table class="table table-sm table-bordered">
        <thead>
        <tr class="text-center">
            <th>Address</th>
            <th>Phone</th>
            <th>Action</th>
        </tr>
        </thead>
        <tbody formArrayName="users">
        <tr *ngFor="let item of usersForm.controls.users.controls; let $index=index" [formGroupName]="$index">
            <td style="min-width: 120px">
                <input class="form-control" type="text" formControlName="address"/>
                <div class="text-danger" *ngIf="usersForm.controls['users'].controls[$index].controls['address'].touched
                             && usersForm.controls['users'].controls[$index].controls['address'].hasError('required')">
                    Please enter address!
                </div>
            </td>

            <td style="min-width: 120px">
                <input class="form-control" type="text" formControlName="address"/>
                <div class="text-danger" *ngIf="usersForm.controls['users'].controls[$index].controls['phone'].touched
                             && usersForm.controls['users'].controls[$index].controls['phone'].hasError('required')">
                    Please enter phone number!
                </div>
            </td>

            <td style="width: 100px">
                <button (click)="addUserRow()" class="btn btn-success btn-sm mr-1" type="button"><i class="fa fa-plus"></i></button>
                <button (click)="removeUserRow($index)" class="btn btn-danger btn-sm" type="button"><i class="fa fa-times"></i></button>
            </td>
        </tr>
        </tbody>
    </table>
</form>

Надеюсь, теперь ваша проблема будет решена!

person TanvirArjel    schedule 27.07.2018

С реактивными формами вы определяете структуру данных для хранения данных формы в вашем коде. В вашем примере вы определяете его с помощью this.form. Но в вашем определении формы есть только один элемент управления адресом и один элемент управления телефоном. Итак, вы сказали Angular хранить только один адрес и один телефон.

Чтобы разрешить использование нескольких наборов адресов / телефонов, вам понадобится FormArray, как указано в @DavidZ. Как следует из названия, FormArray позволяет вам иметь массив значений формы.

В вашем примере ваш FormArray будет состоять из FormGroup с одной записью для каждого пользователя. FormGroup будет состоять из набора FormControls: адреса и телефона.

Компонент формы

В этом примере есть имя и массив адресов, но он должен дать вам общее представление:

ngOnInit(): void {
    this.customerForm = this.fb.group({
        name: ['', [Validators.required, Validators.minLength(3)]],
        addresses: this.fb.array([this.buildAddress()])
    });
}

buildAddress(): FormGroup {
    return this.fb.group({
            addressType: 'home',
            street1: ['', Validators.required],
            street2: '',
            city: '',
            state: '',
            zip: ''
    });
}

Вы можете найти полный код для этого здесь: https://github.com/DeborahK/Angular2-ReactiveForms/tree/master/Demo-Final-Updated

person DeborahK    schedule 27.07.2018

Вы можете использовать FormArray, эта статья дает хорошее резюме.

Также убедитесь, что вы понимаете различия между FormArray и FormGroup

person DavidZ    schedule 27.07.2018