How to use reactive forms within ngFor

To solve your problem you have to use formArray as follows:

In the component:

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);
        }
    }

}

In the view:

<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>

Hope now your problem will be solved!


With reactive forms, you define the data structure to hold the form data in your code. In your example, you define it with this.form. But your form definition only has one address control and one phone control. So you've told Angular to only store one address and one phone.

To allow for multiple address/phone sets, you need a FormArray as mentioned by @DavidZ. As its name implies, a FormArray allows you to have an array of form values.

In your example, your FormArray would be comprised of a FormGroup with one entry for each user. The FormGroup would be comprised of the set of FormControls: the address and the phone.

Form Component

This example has a name and an array of addresses, but should give you a general idea:

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: ''
    });
}

You can find the complete code for this here: https://github.com/DeborahK/Angular2-ReactiveForms/tree/master/Demo-Final-Updated