onmyway
onmyway

Reputation: 1515

How bind to an Id in an Angular Material Autocomplete drop-down, but filter by the string representation

I have an Angular Material Autocomplete drop-down with a filter that filters by CustomerName.

This was implemented via my returned Customers from my getAllCustomers() method. I then do a loop through each Customer to push the CustomerName into a new array, which essentially becomes my filteredOptions.

My question is: How can I implement this filter with the search on the CustomerName, but have a binding to the Id of each Customer?

In the object that I eventually want to save, I want to save the Customer.Id and not the CustomerName.

I have tried creating a new object array, containing both the CustomerName and Id, but this does not work with the filteredOptions and filter method. It seems like the filter method only takes an array with single values and not objects.

Also, I would need to bind this correctly in my HTML.

Here is my basic fileredOptions implementation: (Note: I included my object {name: element.CustomerName, id: element.Id} that I wish to use. This doesn't work as explained. The working method simply pushes element.CustomerName into the array:

filteredOptions: Observable<string[]>;

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

ngOnInit() {
    this.getAllCustomers();
}

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

getAllCustomers() {
  this.loadDataService.getAllCustomers()
  .subscribe(data => {
    this.customerArray = data;
    let thisArray = [];
    this.customerArray.forEach(element => {
      thisArray.push({name: element.CustomerName, id: element.Id});
    });
    this.customerNameArray = thisArray;
    this.filteredOptions = this.myCustomerSearchControl.valueChanges.pipe(
      startWith(''),
      map(val => this.filter(val))
    );
  });
} 

Here 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>

Upvotes: 9

Views: 3711

Answers (1)

G. Tranter
G. Tranter

Reputation: 17918

If you use an object for your options, you will need to modify your filter function and filteredOptions to use the object and not a string array. You will also need to use the displayWith feature of mat-autocomplete to allow the input to work with the object. A stackblitz example is here.

Your code:

export class Customer{
    constructor(public CustomerName: string, public Id: number) { }
}

...

filteredOptions: Observable<Customer[]>;

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

ngOnInit() {
    this.getAllCustomers();
}

filter(val: any) {
    let name = val.CustomerName || val; // val can be Customer or string
    return this.customerNameArray.filter(option => option.CustomerName.toLowerCase().indexOf(name.toLowerCase()) === 0);
}

getAllCustomers() {
    this.loadDataService.getAllCustomers()
    .subscribe(data => {
        this.customerArray = data;
        let thisArray = [];
        this.customerArray.forEach(element => {
            thisArray.push(new Customer(element.CustomerName, element.Id));
        });
        this.customerNameArray = thisArray;
        this.filteredOptions = this.myCustomerSearchControl.valueChanges.pipe(
            startWith(null),
            map(val => this.filter(val))
        );
    });
}

displayCustomer(cust: Customer) {
    return cust ? cust.CustomerName : '';
}

HTML:

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

Upvotes: 4

Related Questions