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.
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.
<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).
Wenn man allerdings in das Eingabefeld hinein klickt, geht das Angular Material Autocomplete Menü, mit all definierten Elementen (Früchten) auf.
Man kann nun mit dem Klick auf eine der Optionen, das gewünschte Element auswählen und die Auswahl steht dann im Eingabefeld.
Die wichtigsten Funktion ist die Autovervollständigung bei diesem Element. Und diese funktioniert auch bei einem einfachen, nativen Eingabefeld.
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
.
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.
<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
.
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.
// 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.
<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.
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.