Aktualisiert am 13.07.2018: Umfassende Überarbeitung des Codes zur Verwendung neuerer Konventionen. Angular und RxJS auf die neuesten verfügbaren Versionen aktualisiert, Beispiele nach StackBlitz verschoben.
Das Ziel dieses Beitrags ist es, zu demonstrieren, wie Angular-Route-Resolves in 10 Minuten funktionieren, zusammen mit der Bereitstellung von Beispielcode auf Stackblitz. Wenn Sie bereits mit Route Resolves aus AngularJS vertraut sind, empfehle ich, die Einleitung zu überspringen und direkt zum Abschnitt mit der Beispiel-App zu springen.
Inhaltsverzeichnis
Intro
Beispiel-App
- Schritt 1: Eine Resolver-Klasse erstellen
- Schritt 2: Einen Resolve Guard zur Route hinzufügen
- Schritt 3: Abrufen der aufgelösten Daten von der aktivierten Route der Komponente
Hinweise & Tipps
- Vermeiden redundanter API-Aufrufe
- Mehrere Auflösungen auf der gleichen Route
- Übergabe von Routenparametern an den Resolver
Abschließende Anmerkungen
Einführung
Routenauflösungen sind nichts anderes als eine Möglichkeit, diedie Daten zu holen, die eine Komponente benötigt, bevor sie initialisiert wird. Normalerweise kommen diese Daten von einer API. Nehmen wir an, Sie haben eine Komponente, deren einzige Aufgabe es ist, ein Diagramm der täglichen Verkäufe für den Monat anzuzeigen; es macht keinen Sinn, die Ansicht zu rendern oder diese Komponente zu laden, bevor die Verkaufsdaten verfügbar sind. Viele Diagrammbibliotheken geben einen Fehler aus, wenn Sie versuchen, ein Diagramm zu initialisieren, bevor Sie es mit Daten versorgen (und das gilt auch für *ngFor
). Natürlich können Sie dieses Problem leicht umgehen, indem Sie die HTML-Datei mit einem *ngIf
ausblenden oder vorübergehend ein leeres Array bereitstellen, bis die erforderlichen Daten geladen sind. Auch wenn man ohne Resolves auskommen kann, hilft die Implementierung von Resolves, den Code lesbarer und wartbarer zu machen, indem:
- verwirrendes Durcheinander in der Vorlage und im Code der Komponente vermieden wird.
- Sie verdeutlichen Ihre Absicht, indem Sie zeigen, welche Daten vorab abgeholt werden müssen.
Trotz dieser Vorteile vermeiden viele Websites die Verwendung von Auflösungen und zeigen stattdessen den größten Teil der Komponente und einen Spinner in den Abschnitten an, für die noch Daten geladen werden müssen. In einigen Fällen ist dieser Ansatz aus UX-Sicht wünschenswert, und eine Auflösung sollte nicht verwendet werden.
Beispiel-App
Beispiel-App öffnen
Wie Sie sehen können, ist unser Ausgangspunkt eine sehr einfache App mit zwei Routen – „Home“ und „News“. Sie navigieren zwischen den Routen, indem Sie auf die entsprechende Registerkarte klicken. Wir werden eine Auflösung hinzufügen, um Nachrichten von einer API zu holen, bevor die Nachrichtenkomponente geladen wird. Dies geschieht in drei Schritten:
Schritt 1: Erstellen einer Resolver-Klasse, die einen Http-Aufruf tätigt, um die benötigten Daten vorab zu holen.
Erstelle eine neue Typescript-Datei im Ordner app
und benenne sie:
news-resolver.service.ts
Dann kopiere den folgenden Code und füge ihn in die neue Datei ein (siehe unten):
Schauen wir uns an, was wir im obigen Code gemacht haben:
-
Hinzufügen von ES6-Import-Anweisungen, um die notwendigen Module einzubinden.
-
Erstellte eine neue TypeScript
NewsResolver
-Klasse. -
Hinzufügen der
Resolve
-Schnittstelle zu unserer Klasse – dies ist OPTIONAL, aber jede Klasse, die wir als Resolver verwenden wollen, muss eine Resolve-Methode implementieren, also ist es eine gute Konvention. -
Hinzufügen einer
resolve()
-Methode zuNewsResolver
– dies ist die Methode, die für die Rückgabe der benötigten Daten verantwortlich ist. Der Name der Methode, die Daten für einen Resolve Guard zurückgibt, ist NICHT optional – der Resolver würde nicht funktionieren, wenn die Methode einen anderen Namen hätte. -
Wenn Sie bemerkt haben, dass im obigen Code fälschlicherweise eine
POST
-Anfrage anstelle einerGET
-Anfrage verwendet wird, haben Sie völlig recht; in einer echten Anwendung wäre dies eineGET
-Anfrage. Die Beispiele hier nutzen, eine Seite, die Test-API-Endpunkte bereitstellt.
Bevor wir weitermachen, müssen wir die Resolver-Klasse, die wir gerade erstellt haben, in unser Routing-Modul aufnehmen. Navigieren Sie zu app-routing.module.ts
und fügen Sie NewsResolver
in das providers
-Array ein. Oder, wenn Sie gerade erst anfangen, mit Angular 2 zu arbeiten, ersetzen Sie einfach den Inhalt von app-routing.module.ts
durch den folgenden Code – Änderungen sind mit Anmerkungen gekennzeichnet:
Damit haben wir die Resolver-Klasse definiert. In den folgenden Schritten fügen wir sie unserer Route hinzu.
Code nach dem Hinzufügen der Resolver-Klasse
Schritt 2: Fügen Sie der Route einen Resolve Guard hinzu.
Ändern Sie in app-routing.module.ts
die folgende Codezeile { path: 'news', component: NewsComponent }
zu:
{ path: 'news', component: NewsComponent, resolve: { news: NewsResolver }}
Alles, was wir hier getan haben, war das Hinzufügen des gerade definierten Resolve Guard zu unserer news
-Route. Das sagt Angular, dass wir warten müssen, bis die NewsResolver
-Methode resolve()
Daten zurückgibt, bevor wir NewsComponent
anzeigen.
Es ist wichtig, darauf hinzuweisen, dass news
in der Zeile news: NewsResolver
des Codes das ist, was ich gewählt habe, um die Daten zu nennen, die vom Resolver zurückgegeben werden. Sie können es benennen, wie Sie wollen.
Als kleine Randbemerkung – wenn Sie mit Angular-Route-Guards im Allgemeinen nicht vertraut sind und mehr wissen möchten, finden Sie die Dokumentation hier. Auf Details zu Guards einzugehen, würde den Rahmen dieses Beitrags sprengen, aber Sie sollten wissen, dass es neben resolve
noch andere Guards gibt, weshalb ich den Begriff erwähnt habe.
Code mit dem hinzugefügten Resolve Guard
Schritt 3: Holen Sie sich die aufgelösten Daten aus der aktivierten Route der Komponente.
Nun, da wir eine Resolver-Klasse haben und sie zu unserer Route hinzugefügt haben, werden die Daten vorab abgeholt. Der letzte Schritt ist der Zugriff auf die vorabgerufenen Daten in unserer NewsComponent
, die sich in app/news.component.ts
befindet. Navigieren Sie zu dieser Datei und fügen Sie das folgende ES6-Modul hinzu:
import { ActivatedRoute } from '@angular/router';
Dann stellen Sie ActivatedRoute
im Konstruktor der News-Komponente bereit, indem Sie den folgenden Code am Anfang der NewsComponent
-Klassendefinition hinzufügen:
constructor(private route: ActivatedRoute) {}
Wie Sie wissen oder vielleicht schon vermutet haben, ermöglicht uns ActivatedRoute
den Zugriff auf Informationen über die derzeit aktive Route, wie z. B. die URL der Route, Abfrageparameter usw. Was uns hier interessiert, sind die Nachrichtendaten, die vom resolve guard in das ActivatedRoute
geladen werden. Um die aufgelösten Nachrichtendaten von der Route zu erhalten, fügen Sie eine Eigenschaft hinzu, die die Nachrichtendaten enthält:
public news: any;
und erhalten dann die Daten von der aktivierten Route, wenn die NewsComponent
initialisiert wird:
ngOnInit(): void { this.news = this.route.snapshot.data; }
Das war’s praktisch. Ändern Sie die Vorlage der Nachrichtenkomponente, um die Nachrichten aus der Routenauflösung anzuzeigen, indem Sie den aktuellen Inhalt entfernen:
<div>This is just a placeholder for now. News will go here. </div>
und durch Folgendes ersetzen:
Sie sollten nun mit der neuesten Nachricht „Der Himmel ist blau“ begrüßt werden, wenn Sie auf die Registerkarte Nachrichten klicken.
Fertiger Code
Hinweise &Tipps
– Auflösungen können zu redundanten API-Aufrufen führen: Ein Resolve ruft jedes Mal Daten ab, wenn eine Komponente geladen wird. Dies führt oft zu unnötigen API-Aufrufen, die sich negativ auf die Leistung auswirken. Wenn Ihre Auflösungsfunktion Daten abruft, die sich nicht häufig ändern, sollten Sie erwägen, die von der Auflösungsfunktion zurückgegebenen Daten in eine Eigenschaft in der Resolver-Klasse zu schreiben und diese Eigenschaft einfach zurückzugeben, wenn sie bereits festgelegt wurde. In unserem Beispiel würden wir eine anfänglich undefinierte Eigenschaft news
wie folgt hinzufügen: public news: any = undefined;
in unserem Nachrichtenauflöser. In der Methode resolve()
prüfen wir dann, ob die Eigenschaft news
bereits festgelegt ist, und geben ihren Wert zurück, ohne einen API-Aufruf zu tätigen, wenn dies der Fall ist, d. h.:
resolve(): Observable<any> { if (this.news) { return this.getSavedNews(); } else { return this.getNewsFromApi() } }
vollständiges Codebeispiel unten. Wenn Ihnen die observable-Syntax etwas ungewöhnlich vorkommt, liegt das daran, dass ich RxJS 6
verwende. Es gibt ein gutes Tutorial über die kürzlich eingeführten Änderungen an RxJS
hier, falls Sie eine Auffrischung benötigen.
Gespeicherte Daten zurückgeben, wenn sie bereits von der API abgeholt wurden
Natürlich könnte man noch weiter gehen und einen Zeitraum festlegen, in dem die Daten gültig sind, indem man die Daten nicht nur speichert, sondern eine weitere Zeitstempel-Eigenschaft hinzufügt und einen API-Aufruf tätigt, wenn die Daten älter als x sind.
– Genau wie in AngularJS kann man mehrere Resolve-Aufrufe für dieselbe Route verwenden. Die Resolve-Aufrufe erfolgen parallel und die Komponente wird erst geladen, wenn alle Aufrufe Daten zurückliefern. Um mehrere Resolves zu verwenden, fügen Sie sie einfach zur Route hinzu:
Dann kann auf zusätzliche Resolve-Daten aus dem Routen-Snapshot zugegriffen werden, genau wie bei einem einzelnen Resolve:
ngOnInit(): void { this.news = this.route.snapshot.data; this.alternativeNews = this.route.snapshot.data; }
Code mit mehreren Resolves
– Der Resolver hat Zugriff auf Routen-Parameter. Nehmen wir an, Sie haben eine Komponente, die eine Liste von Nachrichtentiteln anzeigt. Wenn eine Meldung angeklickt wird, wird eine andere Komponente geöffnet, die den vollständigen Artikel anzeigt. Bevor diese Komponente geladen wird, muss der Inhalt der Nachricht, deren Titel angeklickt wurde, abgerufen werden – dies kann mit der resolve-Methode geschehen. Die Resolver-Klasse hat Zugriff auf die ActivatedRoute
, so dass wir die ID der angeklickten Meldung erhalten können:
resolve(route: ActivatedRouteSnapshot) { let id: any = route.params); return this.getNewsStory(id); }
Das ist ziemlich selbsterklärend. Als Beispiel können Sie sich die neue src/news-story-resolver.service.ts
-Datei unter dem unten stehenden Link ansehen. Die Links zur neuen Komponente wurden auf der Registerkarte Nachrichten (news.component.ts
) hinzugefügt.
Code mit parametrischer Auflösung