Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to I use angular material autocomplete with complex objects?

I am building an application that uses Angular Material in Angular 16. I have ngrx state management gathering an array of objects to pick from. I want the input to display the name property of this object when one is picked from the autocomplete options. I am currently using a matInput with mat-autocomplete.

import { Component, OnInit } from '@angular/core';
import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms';
import {AsyncPipe} from '@angular/common';
import {MatAutocompleteModule} from '@angular/material/autocomplete';
import {MatInputModule} from '@angular/material/input';
import {MatFormFieldModule} from '@angular/material/form-field';
import { TokenNameWithId } from '../../../shared/models/card.model';
import { Observable, combineLatest, debounceTime, map, startWith } from 'rxjs';
import { Store } from '@ngrx/store';
import { selectAllTokenNames } from '../+state/token-names.selectors';

@Component({
  selector: 'tokenator-tokens-dropdown',
  standalone: true,
  imports: [FormsModule,
    MatFormFieldModule,
    MatInputModule,
    MatAutocompleteModule,
    ReactiveFormsModule,
    AsyncPipe
  ],
  providers: [
  ],
  templateUrl: './tokens-dropdown.component.html',
  styleUrl: './tokens-dropdown.component.scss',
})
export class TokensDropdownComponent implements OnInit {
  public tokenNamesControl = new FormControl<string>('', {
    nonNullable: true,
  });
  public filteredTokenNames!: Observable<TokenNameWithId[]>;
  private tokenNames$ = this.store.select(selectAllTokenNames);

  constructor(private store: Store) {
  }

  ngOnInit(): void {
    this.filteredTokenNames = combineLatest([
      this.tokenNamesControl.valueChanges.pipe(startWith('')),
      this.tokenNames$,
    ]).pipe(
      debounceTime(250),
      map(([value, tokenNamesWithId]) => value ? this._filter(value, tokenNamesWithId) : tokenNamesWithId
    ));
  }

  private _filter(value: string, tokenNamesWithId: TokenNameWithId[]): TokenNameWithId[] {
    const filterValue = value.toLowerCase();
    return tokenNamesWithId.filter((tokenNameWithId: TokenNameWithId) => tokenNameWithId.displayName.includes(filterValue));
  }
}
.example-form {
    min-width: 150px;
    max-width: 500px;
    width: 100%;
  }
  
  .example-full-width {
    padding-top: 10px;
    width: 100%;
  }
<form class="example-form">
    <mat-form-field class="example-full-width">
        <mat-label>Token</mat-label>
        <input 
            type="text"
            placeholder="Select a token"
            aria-label="Token name"
            matInput
            [formControl]="tokenNamesControl"
            [matAutocomplete]="auto">
        <mat-autocomplete #auto="matAutocomplete">
            @for (tokenName of filteredTokenNames | async ; track tokenName) {
                <mat-option [value]="tokenName">{{tokenName.displayName}}</mat-option>
            }
        </mat-autocomplete>
    </mat-form-field>
</form>

This results in odd behavior from the matInput where the search in autocomplete works, I can select a object, but the displayed value is [object Object]. This makes sense, since the underlying selected value from the autocomplete is an object, but I would like the input to have a displayed text of tokenName.displayName just like my <mat-options> do.

This is a display of the form input as displayed on the web page after selecting an available option from autocomplete.  The list of autocomplete options displays the proper text but the input selected value is "object Object".

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Is there a way to get the matInput to display a property of an object selected from autocomplete? Alternatively, is there a better pairing of angular material components I should be using when selecting from an array more complex objects that preserves having working autocomplete?

>Solution :

You need to use the [displayWith] property to customize the displayed text for the selected option.

displayFn(tokenName: any): string {
  return tokenName && tokenName.displayName ? tokenName.displayName : '';
}
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
    ...
</mat-autocomplete>

Reference: Setting separate control and display values

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading