navigation Navigation


Autocomplete


Die Angular Material Autocomplete-Komponente erweitert ein normales Texteingabefeld um eine dynamische Vorschlagsliste. Während der Benutzer tippt, erscheint ein Panel mit passenden Optionen, die entweder aus einer festen Liste oder dynamisch gefiltert werden können.

Einfaches Autocomplete

Im nächsten Beispiel wird ein einfaches Beispiel von einem Autocomplete Component gezeigt, ohne erweiterter Konfiguration.

Es werden einige Elemente benötigt, da dieses Component bereits einige an Funktionen mitbringt.

Hinweis: Für die Verwendung von formControl und spezifisch die Möglichkeit der Datenbindung, wird ReactiveFormsModule benötigt.

autocomplete.component.ts
import { Component, OnInit } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { FormControl, ReactiveFormsModule } from '@angular/forms';

import { MatAutocompleteModule } from '@angular/material/autocomplete';

import { Observable, map, startWith } from 'rxjs';

@Component({
    selector: 'app-autocomplete',
    imports: [
        AsyncPipe,
        MatAutocompleteModule,
        ReactiveFormsModule
    ],
    templateUrl: './autocomplete.component.html',
    styleUrl: './autocomplete.component.scss'
})
export class AutocompleteComponent {

    fruitsFormControl = new FormControl('');
    fruitsItems: string[] = [
        'Apfel',
        'Banane',
        'Birne',
        'Kirsche',
        'Melone'
    ];

    filteredFruits!: Observable<string[]>;

    ngOnInit(): void {
        this.filteredFruits = this.fruitsFormControl.valueChanges.pipe(
            startWith(''),
            map(value => this._filterFruitsOptions(value || ''))
        );
    }

    /**
     * Filter fruits options
     * ---
     * @param value - Entered value
     */
    private _filterFruitsOptions(value: string): string[] {
        const filterValue = value.toLowerCase();
        return this.fruitItems.filter(fruit => fruits.toLowerCase().includes(filterValue));
    }

}

In diesem Beispiel wird das native Browser-Input-Element verwendet, ohne einer Integration von Angular Material. Lediglich das Autocomplete-Dropdown-Menü wird von Angular Material bereitgestellt. Das ist in den Fällen nützlich, wenn man eigene Stile oder Corporate Design anwenden möchte/muss.

autocomplete.component.html
<input
    type="text"
    [formControl]="fruitsFormControl"
    [matAutocomplete]="autoFruits"
>
<mat-autocomplete #autoFruits="matAutocomplete">
    @for (fruit of filteredFruits; track fruit) {
        <mat-option [value]="fruit">{{ fruit }}</mat-option>
    }
</mat-autocomplete>

Als Ergebnis würde man, je nach Browser, folgende Ausgabe erhalten (Hinweis: Die Elemente um das Input-Element sind einfach Bestandteile meiner App insgesamt und haben keine Auswirkung auf das gezeigte Component).

Angular Material Component - Autocomplete - Einfaches Beispiel

Wenn man allerdings in das Eingabefeld hinein klickt, geht das Angular Material Autocomplete Menü, mit all definierten Elementen (Früchten) auf.

Angular Material Component - Autocomplete - Dropdown Beispiel

Man kann nun mit dem Klick auf eine der Optionen, das gewünschte Element auswählen und die Auswahl steht dann im Eingabefeld.

Angular Material Component - Autocomplete - Ausgewähltes Element

Die wichtigsten Funktion ist die Autovervollständigung bei diesem Element. Und diese funktioniert auch bei einem einfachen, nativen Eingabefeld.

Angular Material Component - Autocomplete - Autocomplete Beispiel

Einfaches Beispiel - Custom Input

Im nächsten Schritt werden wir mat-form-field aus dem MatInputModule einbinden und das Eingabefeld mit Angular Material Stilen ausgeben.

Der TypeScript-Code erweitert sich also um die Einbindung von MatInputModule.

autocomplete.component.ts
import { Component, OnInit } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { FormControl, ReactiveFormsModule } from '@angular/forms';

import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatInputModule } from '@angular/material/input';

import { Observable, map, startWith } from 'rxjs';

@Component({
    selector: 'app-autocomplete',
    imports: [
        AsyncPipe,
        MatAutocompleteModule,
        MatInputModule,
        ReactiveFormsModule
    ],
    templateUrl: './autocomplete.component.html',
    styleUrl: './autocomplete.component.scss'
})
export class AutocompleteComponent {

    fruitsFormControl = new FormControl('');
    fruitsItems: string[] = [
        'Apfel',
        'Banane',
        'Birne',
        'Kirsche',
        'Melone'
    ];

    filteredFruits!: Observable<string[]>;

    ngOnInit(): void {
        this.filteredFruits = this.fruitsFormControl.valueChanges.pipe(
            startWith(''),
            map(value => this._filterFruitsOptions(value || ''))
        );
    }

    /**
     * Filter fruits options
     * ---
     * @param value - Entered value
     */
    private _filterFruitsOptions(value: string): string[] {
        const filterValue = value.toLowerCase();
        return this.fruitItems.filter(fruit => fruits.toLowerCase().includes(filterValue));
    }

}

Und das HTML-Template wird ebenfalls um die Verwendung von mat-form-field erweitert. An dieser Stelle soll man nicht vergessen matInput Direktive am Input-Element zu platzieren. Dies wandelt das Eingabe-Feld in Angular Material Eingabe-Feld um.

autocomplete.component.html
<mat-form-field>
    <mat-label>Früche</mat-label>
    <input
        matInput
        type="text"
        [formControl]="fruitsFormControl"
        [matAutocomplete]="autoFruits"
    >
    <mat-autocomplete #autoFruits="matAutocomplete">
        @for (fruit of filteredFruits; track fruit) {
            <mat-option [value]="fruit">{{ fruit }}</mat-option>
        }
    </mat-autocomplete>
</mat-form-field>

Mit diesen Änderungen wird das Input-Element über Angular Material bereitgestellt und hat entsprechend alle Stile, APIs und Methoden von MatInputModule.

Angular Material Component - Autocomplete - Custom Input

Beispiel - Verwendung mit Objekten

Angular’s Autocomplete Material Component kann nicht nur mit einfachen Daten, sondern auch mit komplexeren Daten, wie Objekten, verwendet werden.

Im nächsten Beispiel werden Personen-Objekte verwendet, die nach Ihren Vornamen gefiltert werden. Dabei werden wir eine angepasste Ausgabe bei einer Auswahl im Autocomplete platzieren und zusätzlich an einer anderen Stelle ausgeben, welches Element ausgewählt wurde.

Zuerst definieren wir unsere TypeScript-Klasse mit entsprechenden Personen-Objekten. Was neu hinzukommt, ist der MatAutocompleteSelectedEvent, mit welchem wir auf die Auswahl reagieren können.

Zusätzlich wird ein Interface IPerson definiert, damit wir diesen Typ verwenden können.

autocomplete.component.ts
// Angular
import { Component, OnInit } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { FormControl, ReactiveFormsModule } from '@angular/forms';

// Angular material
import {
    MatAutocompleteModule,
    MatAutocompleteSelectedEvent
} from '@angular/material/autocomplete';
import { MatInputModule } from '@angular/material/input';

// RxJS
import { Observable, map, startWith } from 'rxjs';

// Person interface
interface IPerson {
    name: string;
    age: number;
}

@Component({
    selector: 'app-autocomplete',
    imports: [
        AsyncPipe,
        MatAutocompleteModule,
        MatAutocompleteSelectedEvent,
        MatInputModule,
        ReactiveFormsModule
    ],
    templateUrl: './autocomplete.component.html',
    styleUrl: './autocomplete.component.scss'
})
export class AutocompleteComponent {

    formControl = new FormControl('');
    persons: IPerson[] = [
        { name: 'Alice', age: 30 },
        { name: 'John', age: 34 },
        { name: 'Tony', age: 25 },
        { name: 'Henry', age: 56 },
        { name: 'Russel', age: 40 }
    ];
    filteredPersons!: Observable<IPerson[]>;
    selectedPerson: IPerson | undefined = undefined;

    ngOnInit(): void {
        this.filteredPersons = this.formControl.valueChanges.pipe(
            startWith(''),
            map(value => this._filterPersons(value || ''))
        );
    }

    /**
     * Filter persons
     * ---
     * @param value - Entered value
     */
    private _filterPersons(value: string): string[] {
        const filterValue = value.toLowerCase();
        return this.persons.filter(p => p.name.toLowerCase().includes(filterValue));
    }

    /**
     * Display function - Persons
     * ---
     * @param person - Selected person object
     * ---
     * @returns {string}
     */
    displayFnPerson(person: IPerson): string {
        return person && person.name ? `${person.name} ${person.age ? '(' + person.age + ')' : ''}` : '';
    }

    /**
     * Update option selected
     * ---
     * @param event - Option select event 
     */
    updatePersonSelected(event: MatAutocompleteSelectedEvent): void {
        this.selectedPerson = event.option.value;
    }

}

Was wir in diesem Component ebenfalls hinzugefügt habe, die die displayFnPerson Methode, welche für die korrekte Anzeige im Autocomplete-Feld bei einer Auswahl zuständig ist. Wenn wir mit Objekten arbeiten, weiß Autocomplete nicht automatisch, wie die Anzeige behandelt werden soll und daher sieht man manchmal hier die Ausgabe [Object object].

Im Template werden wir nun unser mat-autocomplete im mat-form-field aufbauen und auch den [displayWith] Input verwenden, um unsere displayFnPerson Funktion für die Anzeige zu verwenden.

autocomplete.component.html
<mat-form-field>
    <mat-label>Personen</mat-label>
    <input
        matInput
        type="text"
        [formControl]="formControl"
        [matAutocomplete]="autoPersons"
    >
    <mat-autocomplete
        #autoPersons="matAutocomplete"
        [displayWith]="displayFnPerson"
        (optionSelected)="updatePersonSelected($event)"    
    >
        @for (person of persons; track person.name) {
            <mat-option [value]="obj">{{ obj.name }}</mat-option>
        }
    </mat-autocomplete>
</mat-form-field>

<p>Current selected: {{ selectedPerson ? selectedPerson.name : '---' }}</p>

Als Ergebnis haben wir mit dieser Konfiguration von Autocomplete Component folgende Ausgabe, in der die Personen-Objekte korrekt mit dem Namen in der Liste repräsentiert werden.

Angular Material Component - Autocomplete - Objekt Liste

Und wenn man eine Person oder ein Objekt auswählt, wird die displayFnPerson (in diesem Beispiel) getriggert und sorgt, für eine angepasste Ausgabe. Hier wird, neben dem Namen, das Alter der Person, wenn verfügbar, in runden Klammern ausgegeben.

Angular Material Component - Autocomplete - Ausgewähltes Objekt Anzeige