angularangular-reactive-formsangular-formscontrolvalueaccessor

How to remove nested sub-form from the sub-form itself?


I have a parent form that contains a formArray which can I dynamically append additional formGroups to on click of a button that calls addForm():

ngOnInit() {
  this.signupForm = this.formBuilder.group({
    formList: this.formBuilder.array([this.formListGroupDef()]),
  });
}

formListGroupDef() {
  return this.formBuilder.control(
    {
      firstName: '',
      email: '',
    },
    Validators.required
  );
}

// Triggered on click of a button in the HTML
addForm() {
  this.formList.push(this.formBuilder.control({ firstName: '', email: '' }));
}

In the parent form template, I iterate through the formArray and render them using the nested sub-form app-profile-form that implements ControlValueAccessor

<ng-container formArrayName="formList">
  <div *ngFor="let fg of formList.controls; let infoIndex = index">
    <app-profile-form
      [formControlName]="infoIndex"
      [formIndex]="infoIndex"
    ></app-profile-form>
  </div>
</ng-container>

app-profile-form template:

<div [formGroup]="form">
  <label for="first-name">First Name</label>
  <input formControlName="firstName" id="first-name" />
  <div *ngIf="firstNameControl.touched && firstNameControl.hasError('required')" class="error">
    First Name is required
  </div>

  <label for="email">Email</label>
  <input formControlName="email" type="email" id="email" />
  <div *ngIf="emailControl.touched && emailControl.hasError('required')" class="error">
    Email is required
  </div>

  <!-- delete the sub-form on click of this button and update the formList array in the parent form --> 
  <button name="deleteForm" (click)="delete()">Delete Form</button>
</div>

As the sub-form can be used on different parent forms, I need to be able to delete the sub-form within itself on the click of the deleteForm button and update the formList array in the parent form. How can I achieve this?

Here's my stackblitz example. I need to implement the delete() function in profile-form.component.ts


Solution

  • You can have nice work with DI here, which is the power of Angular. So, just inject NgControl and get its parent. Here is the solution:

    profile-form.component.ts

    ...
    constructor(private formBuilder: FormBuilder, private injector: Injector)
    ...
    
    delete() {
      // get the current NgControl directive here
      const controlName = this.injector.get(NgControl);
      // get the current FormArrayName directive here and get its control
      const formArray = this.injector.get(FormArrayName).control;
      // find the index, if your control exists in the FormArray
      const index = formArray.controls.findIndex((control: AbstractControl) => control === controlName.control);
    
      // and what you actually need to do
      if (index > -1) {
        formArray.removeAt(index);
      }
    }