Kleine Einführung in Angular Resolver

Klemens Kühle
coodoo
Published in
3 min readApr 22, 2020

--

In der ewig langen Dokumentation Angulars gibt es unter Routing & Navigation bei Milestone 5: Route guards den Punkt Resolve. Er wird überschrieben mit dem Stichpunkt “Fetch data before navigating” und genau das ist was Resolver bieten (edit 2021: Aktuelle Doku dazu ist umgezogen unter Tutorials).

Detailansicht Funktionalität Resolver

Bedeutung

Bevor eine Komponente geladen wird, kann über einen Resolver ein für die Komponente benötigter API-Call durchgeführt werden, dessen Daten dann in der Route zwischengespeichert werden.

Nutzen

Ein Vorteil davon ist, dass eine aufzurufende Seite zum Beispiel nicht angezeigt werden muss, wenn der API-Call fehlschlägt oder die angeforderten Daten nicht gefunden werden, denn diese Fälle kann man im Resolver abfangen, das angeforderte Routing verhindern und den Benutzer umleiten.

Ein weiterer Vorteil ist, dass man den Zwischenspeicher der Route nutzen kann, sodass keine weiteren unnötigen API-Calls für die gleichen Daten abgesetzt werden müssen.

Verwendung — Szenario

Hier demonstrieren will ich hauptsächlich den zweiten Vorteil, da der erste gut in der oben verlinkten Angular-Anleitung dokumentiert ist. Für das Beispiel nutze ich ein Szenario einer Spieler-Detailseite, die verschiedene Unterpunkte hat:

PlayerComponent
PlayerStatsComponent
PlayerDataComponent

Spielerprofil des Leon in unserer HALBZEIT.app Pub-Tipprunde

Die PlayerComponent lädt die generellen Daten des aktuellen Players, wie zum Beispiel ID, E-Mail-Adresse und Namen. Die Kind-Komponente PlayerStatsComponent zeigt den Spielernamen an, aber lädt zusätzlich Statistiken. Die andere Kind-Komponente PlayerDataComponent zeigt die Daten des Spielers in einem Formular an, um sie bearbeiten zu können.

Verwendung — Implementierung

Einen Resolver kann man sich als Service-Klasse mit der Angular CLI sehr leicht generieren lassen mit folgendem Befehl:

ng generate service player/player-resolver

Hier implementieren wir nun das Resolve-Interface mit unserem gewünschten Datentyp Player. Dieses Interface bietet die Methode resolve an. In dieser passiert lediglich unser API-Call, dessen Abhängigkeiten, wie z.B. den passenden Service, wir noch einbinden müssen und fertig ist der Resolver:

import { Injectable } from@angular/core’;
import { ActivatedRouteSnapshot, Resolve } from@angular/router’;
import { Player } from ‘xyz/player.model’;
import { PlayerService } from ‘xyz/player.service’;
@Injectable({providedIn: 'root'})
export class PlayerResolver implements Resolve<Player> {
constructor(private ps: PlayerService) {}
resolve(ars: ActivatedRouteSnapshot) {
return this.ps.getPlayer(ars.paramMap.get(‘playerId’));
}
}

Der jeweiligen Route muss nun nur noch mitgeteilt werden, dass der Resolver verwendet werden soll. Dazu den neuen Resolver in der Routes-Definition importieren und einem passenden Namen player zuweisen.

import { PlayerResolver } from './player.resolver';const routes: Routes = [{
path: ‘player/:playerId’,
component: PlayerComponent,
resolve: {
player: PlayerResolver
},
children: [{
path: 'stats',
component: PlayerStatsComponent
}, [...]]
}];

Nun wird beim Aufrufen der player/:playerId-Route vorm Laden der eigentlichen Komponente der API-Call abgesetzt und das Ergebnis als player im data-Teil der Route gespeichert. Die Komponenten können nun ganz einfach und synchron wie folgt auf die Daten zugreifen:

this.player = this.activatedRoute.snapshot.data[‘player’];

Fazit

Für mein Szenario war diese Lösung ein guter Weg um die Anzahl der API-Calls drastisch zu minimieren. Die Funktionalität des Resolvers als eigentlicher Route-Guard bietet zudem die Möglichkeit Aufrufe zu nicht vorhandenen Playern zu verhindern, was wir künftig wohl auch noch implementieren werden.

Resolver bieten also mit wenig Aufwand einiges an Optimierung einer Angular-Anwendung!

--

--