I am working on a form in Angular 14.
The form has a main section, containing the user's essential data, and a secondary section, where every user can add additional data (an array of residences that I display in a table), before submitting the form.
This additional data is optional, the fields are not required.
In form.component.ts
I have:
import { Component } from '@angular/core';
import { FormGroup, FormControl, Validators, FormArray } from '@angular/forms';
@Component({
selector: 'my-app',
templateUrl: './form.component.html',
styleUrls: ['./form.component.css'],
})
export class FormComponent {
public form: FormGroup = new FormGroup({
first_name: new FormControl('', Validators.required),
last_name: new FormControl('', Validators.required),
email: new FormControl('', [Validators.required, Validators.email]),
phone: new FormControl('', Validators.required),
residences: new FormArray([
new FormGroup({
city: new FormControl(''),
address: new FormControl(''),
}),
]),
});
get residencesArray(): FormArray {
return this.form.get('residences') as FormArray;
}
constructor() {}
ngOnInit(): void {}
public sendFormData() {
console.log(this.form.value);
}
addResidence() {
this.residencesArray.push(
new FormGroup({
city: new FormControl(''),
address: new FormControl(''),
})
);
console.log('Residences');
console.log(this.residencesArray.value);
}
}
In the form.component.html
file I have:
<form [formGroup]="form" >
<mat-form-field appearance="outline" floatLabel="always">
<mat-label >Fast name:</mat-label>
<input matInput formControlName="first_name" />
</mat-form-field>
<mat-form-field appearance="outline" floatLabel="always">
<mat-label >Last name:</mat-label>
<input matInput formControlName="last_name" />
</mat-form-field>
<mat-form-field appearance="outline" floatLabel="always">
<mat-label >Email:</mat-label>
<input matInput formControlName="email" />
</mat-form-field>
<mat-form-field appearance="outline" floatLabel="always">
<mat-label >Phone:</mat-label>
<input matInput formControlName="phone" />
</mat-form-field>
<table *ngIf="this.residencesArray.value.length > 1" >
<thead>
<tr >
<th>City</th>
<th>Address</th>
</tr>
</thead>
<tbody>
<tr
*ngFor="
let residence of this.residencesArray.value.slice(0, -1);
let i = index
"
>
<td>{{ residence.city }}</td>
<td>{{ residence.address }}</td>
</tr>
</tbody>
</table>
<div >
<h4>Residences</h4>
<button
(click)="addResidence()"
mat-raised-button
color="primary"
[disabled]="!form.valid"
>
Add residence
</button>
</div>
<div
formArrayName="residences"
*ngFor="let residence of residencesArray.controls; let i = index"
>
<div [formGroupName]="i">
<mat-form-field appearance="outline" floatLabel="always">
<mat-label >City:</mat-label>
<input matInput formControlName="city" />
</mat-form-field>
<mat-form-field appearance="outline" floatLabel="always">
<mat-label >Address:</mat-label>
<input matInput formControlName="address" />
</mat-form-field>
</div>
</div>
<div >
<button
(click)="sendFormData()"
mat-raised-button
color="primary"
[disabled]="!form.valid"
>
Submit
</button>
</div>
</form>
See this Stackblitz for more details.
The problem
The problem with the above is that the array this.residencesArray.value
will always have the last object "invalid": with empty strings for values.
Questions
- Is there a reliable way to get rid of this last object before submitting the form?
- How can I keep the Add residence button disabled as long as the last 2 form fields are empty (but the form is valid)?
CodePudding user response:
Something like this could work:
public sendFormData() {
for (
let index = this.form.value.residences.length - 1;
index >= 0;
--index
) {
const {city, address} = this.form.value.residences[index];
if (!city && !address) {
this.residencesArray.removeAt(index);
}
}
console.log(this.form.value);
}
The idea is to loop through residences (backward) and remove any index where "city" and "address" is not filled. The criteria can be further tweaked.
Working Stackblitz
CodePudding user response:
Another way (but I love the Robert's answer)
submit(form)
{
const values=form.value.residences.filter((x:any)=>x.city || x.address)
while (this.residences.controls.length>values.length)
this.residences.removeAt(0)
this.residences.setValue(values)
}
NOTE: Really if only want to send the values we can not change the formArray, only send de data we can make something like:
submit(form)
{
const data={...form.value,
residences:form.value.residences.filter(
(x:any)=>x.city || x.address)
}
myService.update(data)
}