How to use reactive forms within ngFor
Solution 1
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!
Solution 2
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
Related videos on Youtube
Present practice
Updated on June 04, 2022Comments
-
Present practice almost 2 years
An array of users can have multiple elements (users). In a template, I iterate over this array and add a form for each element. Right now each form binds to the same form group values. If I fill out the form for the first user, submit buttons of all the forms become enabled. How to point each form to the unique formGroup or unique variables?
component:
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 } }
Template:
<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>