0

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" class="my-form">
  <mat-form-field appearance="outline" floatLabel="always">
    <mat-label class="mat-label">Fast name:</mat-label>
    <input class="mat-input" matInput formControlName="first_name" />
  </mat-form-field>

  <mat-form-field appearance="outline" floatLabel="always">
    <mat-label class="mat-label">Last name:</mat-label>
    <input class="mat-input" matInput formControlName="last_name" />
  </mat-form-field>

  <mat-form-field appearance="outline" floatLabel="always">
    <mat-label class="mat-label">Email:</mat-label>
    <input class="mat-input" matInput formControlName="email" />
  </mat-form-field>

  <mat-form-field appearance="outline" floatLabel="always">
    <mat-label class="mat-label">Phone:</mat-label>
    <input class="mat-input" matInput formControlName="phone" />
  </mat-form-field>

  <table *ngIf="this.residencesArray.value.length > 1" class="table">
    <thead>
      <tr class="table-headers">
        <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 class="residences-header">
    <h4>Residences</h4>
    <button
      (click)="addResidence()"
      mat-raised-button
      color="primary"
      [disabled]="!form.valid"
    >
      Add residence
    </button>
  </div>

  <div
    class="residence"
    formArrayName="residences"
    *ngFor="let residence of residencesArray.controls; let i = index"
  >
    <div [formGroupName]="i">
      <mat-form-field appearance="outline" floatLabel="always">
        <mat-label class="mat-label">City:</mat-label>
        <input class="mat-input" matInput formControlName="city" />
      </mat-form-field>

      <mat-form-field appearance="outline" floatLabel="always">
        <mat-label class="mat-label">Address:</mat-label>
        <input class="mat-input" matInput formControlName="address" />
      </mat-form-field>
    </div>
  </div>

  <div class="center">
    <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

  1. Is there a reliable way to get rid of this last object before submitting the form?
  2. How can I keep the Add residence button disabled as long as the last 2 form fields are empty (but the form is valid)?

2 Answers 2

1

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

Sign up to request clarification or add additional context in comments.

Comments

1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.