angularangular-materialautocompletemat-autocomplete

mat-auto complete show name but send value


An auto-complete select that show a list of options. Each option is an object. I want to show the name of each option but send it's Id. This code works. But once the option is selected, the input shows the id. I want to show the name.

Documentation suggests the use of [displayWith]="displayFn" but it require to change [value]="freespin.id" by [value]="freespin" and I don't want that.

I have not found a solution yet.

                  <mat-form-field>
                    <mat-label>Freespin</mat-label>
                    <input type="text"
                           placeholder="Select a freespin"
                           matInput
                           formControlName="freespin"
                           [matAutocomplete]="auto">
                    <mat-autocomplete requireSelection #auto="matAutocomplete">
                        <mat-option *ngFor="let freespin of filteredFreespins[i] | async" [value]="freespin.id">
                            {{freespin.name}}
                        </mat-option>
                    </mat-autocomplete>
                </mat-form-field>

Solution

  • Could you try using the displayWith attribute, please find below a working example!

    html

    <form class="example-form">
      <input
        type="text"
        placeholder="Search for a street"
        [formControl]="control"
        [matAutocomplete]="auto"
        class="example-input"
      />
      <mat-autocomplete
        #auto="matAutocomplete"
        [displayWith]="displayWith.bind(this)"
      >
        @for (street of filteredStreets | async; track street) {
        <mat-option [value]="street.id">{{street.name}}</mat-option>
        }
      </mat-autocomplete>
    </form>
    

    ts

    import { Component, OnInit } from '@angular/core';
    import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
    import { Observable } from 'rxjs';
    import { startWith, map } from 'rxjs/operators';
    import { AsyncPipe } from '@angular/common';
    import { MatAutocompleteModule } from '@angular/material/autocomplete';
    
    /**
     * @title Plain input autocomplete
     */
    @Component({
      selector: 'autocomplete-plain-input-example',
      templateUrl: 'autocomplete-plain-input-example.html',
      styleUrls: ['autocomplete-plain-input-example.css'],
      standalone: true,
      imports: [FormsModule, MatAutocompleteModule, ReactiveFormsModule, AsyncPipe],
    })
    export class AutocompletePlainInputExample implements OnInit {
      control = new FormControl('');
      streets: any[] = [
        { id: 1, name: 'Champs-Élysées' },
        { id: 2, name: 'Lombard Street' },
        { id: 3, name: 'Abbey Road' },
        { id: 4, name: 'Fifth Avenue' },
      ];
      filteredStreets: Observable<any[]>;
    
      ngOnInit() {
        this.filteredStreets = this.control.valueChanges.pipe(
          startWith(''),
          map((value) => this._filter(value || ''))
        );
      }
    
      private _filter(value: string): string[] {
        const filterValue = this._normalizeValue(value);
        return this.streets.filter((street) =>
          this._normalizeValue(street.name).includes(filterValue)
        );
      }
    
      displayWith(val: any) {
        if (val) {
          const found = this.streets.find((x: any) => x.id === val);
          return found && found.name ? found.name : val;
        } else {
          return val;
        }
      }
    
      private _normalizeValue(value: string): string {
        return value.toLowerCase().replace(/\s/g, '');
      }
    }
    

    stackblitz