Search code examples
angularangular-material-5

How set up Angular Material Autocomplete (drop-down) to work with data returned from data call (filteredOptions called to early)


I am trying to create a drop-down list of customers (or anything), making use of Angular 5 Material's Autocomplete functionality. But unlike the examples provided on the Angular website, my data is not static, but gets returned after a data call getAllCustomer().

The problem I am having, seems to be the assigning of the filterOptions before the data has been returned from my getAllCustomer() method.

How can I make sure to only assign my filterOptions after my data returns?

Here is my code:

filteredOptions: Observable<string[]>;

constructor(private loadDataService: LoadDataService, private assetDataService: AssetDataService, private router: Router, private toastr: ToastrService) { }

ngOnInit() {
    this.getAllCustomers();
    this.filteredOptions = this.myCustomerSearchControl.valueChanges.pipe(
      startWith(''),
      map(val => this.filter(val))
    );
}

filter(val: string): string[] {
    return this.customerArray.filter(option => option.toLowerCase().indexOf(val.toLowerCase()) === 0);
}

getAllCustomers() {
    this.loadDataService.getAllCustomers()
    .subscribe(data => {
      this.customerArray = data;
    });
}

This is my HTML:

<mat-form-field>
    <input type="text" placeholder="Customer Search" aria-label="Number" matInput [formControl]="myCustomerSearchControl" [matAutocomplete]="auto">
        <mat-autocomplete autoActiveFirstOption #auto="matAutocomplete">
            <mat-option *ngFor="let option of filteredOptions | async" [value]="option">
            {{ option }}
        </mat-option>
    </mat-autocomplete>
</mat-form-field>

And as a bonus, how would I be able to implement the same, but with an actual search function that returns the data as a user types in the search box - i.e., a search by string method?

This is my searchByString function:

searchForCustomerByString(string) {
    this.loadDataService.getCustomer(string)
      .subscribe(data => {
        this.returnedCustomers = data;
      });
}

Solution

  • You can define the variable in the result of the subscribe like this:

    getAllCustomers() {
        this.loadDataService.getAllCustomers()
        .subscribe(data => {
          this.customerArray = data;
          this.filteredOptions = this.myCustomerSearchControl.valueChanges.pipe(
              startWith(''),
              map(val => this.filter(val))
          );
        });
    }
    

    But the variable filteredOptions might not be initialized so maybe you can use something like a BehaviorSubject to initialize the variable.