Search code examples
angularangular-materialangular-forms

How to use AutoComplete inside ngFor form


I'm using Angular Material AutoComplete module and I've made it work when using with a simple input. But I really want to use it inside a ngFor inside a module.

I've managed to make it almost work, but I can't make it filter the options. This is my html:

<form novalidate [formGroup]="productForm" (ngSubmit)="onSubmit()">
  <mat-card>
    <mat-card-content>
      <mat-form-field>
        <input type="text" matInput [matDatepicker]="picker" formControlName="data" required>

        <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
        <mat-datepicker #picker></mat-datepicker>
      </mat-form-field>

      <mat-divider></mat-divider>

      <!-- Product -->
      <div formArrayName="products">
        <div *ngFor="let item of productForm.get('products').controls; let i = index">
          <div [formGroupName]="i">
            <mat-form-field>
              <input type="text" placeholder="product" matInput formControlName="product" [matAutocomplete]="product" required>
              <mat-autocomplete #product="matAutocomplete" [displayWith]="displayFn">
                <mat-option *ngFor="let option of filterProduct | async" [value]="option">
                  {{option.nome}}
                </mat-option>
              </mat-autocomplete>
            </mat-form-field>

            <mat-divider></mat-divider>
          </div>
        </div>
      </div>

      <button mat-raised-button color="primary" [disabled]="productForm.disabled" type="button" (click)="newProduct()">New Product</button>
    </mat-card-content>

    <mat-card-actions>
      <button mat-raised-button color="primary" [disabled]="productForm.disabled">Add</button>
    </mat-card-actions>
  </mat-card>
</form>

And the component:

public products: any;
public productForm: FormGroup;
public filterProduct: Observable<any>;

constructor(
    private _store: StoreService,
) {
    this.products = this._store.insumo;
}

ngOnInit() {
    this.productForm = new FormGroup({
    data: new FormControl(new Date(), Validators.required),
    products: new FormArray([]),
    });

    this.newProduct();

    this.filterProduct= this.productForm.get('product').valueChanges
    .pipe(
        startWith(''),
        map(value => typeof value === 'string' ? value : value.nome),
        map(nome => nome ? this._filterProduct(nome) : this.insumos.slice())
    );
}

private _filterProduct(nome: string) {
    const _filterValue = nome.toLowerCase();

    return this.products.filter(option => option.nome.toLowerCase().includes(_filterValue));
}

public displayFn(object): string | undefined {
    return object ? object.nome : undefined;
}

public newProduct():void {
    const control = <FormArray>this.productForm.get('products');

    control.push(new FormGroup({
    product: new FormControl(null, Validators.required),
    }));
}

public onSubmit() {
    console.log(this.productForm);
}

I believe the problem is with the function filterProduct, because I need to get the input value, since it's inside a ngFor I don't know how to do it.


Solution

  • From what I can see, you have multiple inputs in a FormArray, each one needs a AutoComplete, which means you need an array of AutoCompletes.

    It seems you want to have a AutoComplete for all inputs. That seems difficult for me, since each input may have different value. It's true that only one autocomplete will be active at one time, but then you need to know which input is in focus, and I do not know how to do that.

    So based on that I try to implement an array of AutoComplete. you can see the result here. https://stackblitz.com/edit/template-angular7-material-primeng-5rfzmd

    I put a mock data there

     this.products = [{ nome: 'ab' }, { nome: 'cd' }, { nome: 'abcd' }];
    

    so you can try the autocomplete.