Search code examples
angularangular-reactive-formsformarray

How to filter angular FormArray data which is displayed in a table


I want when I type the name in the name input field to filter the FormArray data, and sort to what has been typed in the input box form FormArray data controls

enter image description here

 <tbody formArrayName="cards">
                  <tr class="custom" *ngFor="let card of cardsArray().controls; index as i; " [formGroupName]="i">
                    <td class="pr-0">
                      <input [attr.id]="'name'+i" class="form-control form-control-sm" formControlName="name"
                        [readonly]="true">
                    </td>
                    </tr>
                    </tbody>

Solution

  • Observables to the rescue! :)

    Attach a form control to your search field, listen to the changes of it and filter the values. Return an observable to the template and use the async pipe there. Here is a sample for you, you just need to change the variable names to fit your needs:

    The input with the form control:

    <input [formControl]="searchCtrl" placeholder="Search"/>
    

    Let's say your form looks like this:

    this.myForm = this.fb.group({
      formArr: this.fb.array([
        this.fb.group({
          formCtrl: ['one']
        }),
        //... more
      ])
    });
    
    // just a getter for your formarray
    get formArr() {
      return (this.myForm.get('formArr') as FormArray).controls;
    }
    

    Then listen in the component for the change and do the above mentioned filter. I like to put a slight debounce time before making the filter, if the user types fast.

    Then the filtered formArr$ variable (which is an observable):

    formArr$ = this.searchCtrl.valueChanges.pipe(
      startWith(''),
      debounceTime(300),
      switchMap((val: string) => {
        return of(this.formArr as AbstractControl[]).pipe(
          map((formArr: AbstractControl[]) =>
            formArr.filter((group: AbstractControl) => {
              return group.get('formCtrl').value
               .toLowerCase()
               .includes(val.toLowerCase());
            })
          )
        );
      })
    );
    

    Then just use the async pipe in template:

    <div *ngFor="let group of formArr$ | async">
      <div formArrayName="formArr">
        <div [formGroup]="group">
          <input formControlName="formCtrl">
        </div>
      </div>
    </div>
    

    That's it! Here is a DEMO with the above code