Bijgewerkt op 07/13/2018: Uitgebreide revisie van de code om nieuwere conventies te gebruiken. Angular en RxJS geüpgraded naar de laatste beschikbare versies, voorbeelden verplaatst naar StackBlitz.
Het doel van deze post is om in 10 minuten te demonstreren hoe Angular-route-resolves werken, samen met het verstrekken van voorbeeldcode op Stackblitz. Als je al bekend bent met route resolves van AngularJS, raad ik aan de intro over te slaan en direct naar de voorbeeld app sectie te springen.
Table of Contents
Intro
Sample App
- Step 1: Maak een Resolver Class
- Step 2: Voeg een Resolve Guard toe aan de Route
- Step 3: Haal de opgeloste gegevens op uit de geactiveerde Route van het Component
Notes & Tips
- Vermijd overbodige API-aanroepen
- Meerdere Resolves op dezelfde route
- Routeparameters doorgeven aan de Resolver
Sluitende opmerkingen
Intro
Route-resolves zijn niets meer dan een manier om voorafgegevens op te halen die een component nodig heeft voordat het geïnitialiseerd wordt. Meestal komen deze gegevens van een API. Stel dat je een component hebt waarvan de enige rol is om een grafiek weer te geven van de dagelijkse verkoop voor de maand; Het heeft geen zin om de view te renderen of deze component te laden voor de verkoopsgegevens beschikbaar zijn. In feite zullen veel bibliotheken voor grafieken een foutmelding geven als je probeert een grafiek te initialiseren voordat de gegevens beschikbaar zijn (en dat geldt ook voor *ngFor
). Natuurlijk kun je dit probleem gemakkelijk omzeilen door de html te verbergen met een *ngIf
of tijdelijk een lege array te leveren totdat de benodigde gegevens zijn geladen. Hoewel je ook zonder resolves uit de voeten kunt, helpt het implementeren ervan om code leesbaarder en beter onderhoudbaar te maken door:
- Het elimineren van verwarrende rommel in de template en code van je component.
- Het verduidelijken van uw bedoeling door te laten zien welke gegevens moeten worden pre-fetched.
Ondanks deze voordelen, veel sites vermijden het gebruik van resolves in het voordeel van het weergeven van het grootste deel van de component en het tonen van een spinner in de secties waarvoor de gegevens nog worden geladen. In sommige gevallen is deze aanpak wenselijk vanuit een UX-oogpunt, en zou een resolve niet moeten worden gebruikt. Gebruik uw discretie.
Sample App
Open Sample App
Zoals u kunt zien, is ons uitgangspunt een zeer eenvoudige app met twee routes – “Home” en “News”. Je navigeert tussen de routes door op het corresponderende tabblad te klikken. We gaan een resolve toevoegen om nieuws te pre-fetchen van een API voordat de News component laadt. Dit gebeurt in drie stappen:
Stap 1: Maak een resolver-klasse die een http-oproep maakt om de gegevens die we nodig hebben vooraf op te halen.
Maak een nieuw Typescript-bestand aan in de map app
en geef het een naam:
news-resolver.service.ts
Kopieer en plak vervolgens de volgende code in uw nieuwe bestand (wordt hieronder uitgelegd):
Laten we eens bekijken wat we in de bovenstaande code hebben gedaan:
-
Er zijn ES6-import-statements toegevoegd om de benodigde modules binnen te halen.
-
Een nieuwe TypeScript
NewsResolver
-klasse gemaakt. -
De
Resolve
-interface aan onze klasse toegevoegd – dit is OPTIONEEL, maar elke klasse die we als resolver willen gebruiken, moet een resolver-methode implementeren, dus het is een goede conventie. -
Een
resolve()
-methode toegevoegd aanNewsResolver
– dit is de methode die verantwoordelijk is voor het retourneren van de gegevens die we nodig hebben. De methode die gegevens retourneert voor een resolbewaker “resolve” noemen is NIET optioneel – de resolver zou niet werken als de methode een andere naam had. -
Als het u is opgevallen dat in de bovenstaande code ten onrechte een
POST
-verzoek wordt gebruikt in plaats van eenGET
, hebt u helemaal gelijk; in een echte app zou dit eenGET
-verzoek zijn. De voorbeelden hier maken gebruik van, een site die test-API-eindpunten biedt.
Voordat we verder gaan, moeten we de resolverklasse die we zojuist hebben gemaakt, opnemen in onze routingmodule. Navigeer naar app-routing.module.ts
en voeg NewsResolver
toe aan de providers
array. Of, als je net begint te werken met Angular 2, vervang de inhoud van app-routing.module.ts
door de code hieronder – wijzigingen zijn gemarkeerd met notities:
Met dit gedaan, hebben we nu de resolver klasse gedefinieerd. In de volgende stappen zullen we deze toevoegen aan onze route.
Code na het toevoegen van de Resolver Class
Stap 2: Voeg een Resolve Guard toe aan de route.
In app-routing.module.ts
, verander de volgende regel code { path: 'news', component: NewsComponent }
in:
{ path: 'news', component: NewsComponent, resolve: { news: NewsResolver }}
Het enige wat we hier hebben gedaan is het toevoegen van de resolver guard die we zojuist hebben gedefinieerd aan onze news
route. Dit vertelt Angular dat we moeten wachten op NewsResolver
’s resolve()
methode om gegevens terug te geven voordat we de NewsComponent
weergeven.
Het is belangrijk om erop te wijzen dat news
in de regel news: NewsResolver
van code is wat ik heb gekozen om de naam te geven van de gegevens die door de resolver worden geretourneerd. Je kunt het elke naam geven die je wilt.
Als een beetje een tangentiële opmerking – als je niet bekend bent met Angular route guards in het algemeen, en meer zou willen weten, is de documentatie hier. In detail treden over guards valt buiten het bestek van deze post, maar je moet weten dat er naast resolve
nog andere guards beschikbaar zijn, vandaar dat ik de term ter sprake bracht.
Code With The Resolve Guard Added
Stap 3: Haal de opgeloste gegevens op uit de geactiveerde route van de component.
Nu we een resolverklasse hebben en deze aan onze route hebben toegevoegd, worden gegevens vooraf opgehaald. De laatste stap is om toegang te krijgen tot de vooraf opgehaalde gegevens in onze NewsComponent
die zich in app/news.component.ts
bevindt. Navigeer naar dat bestand en voeg de volgende ES6-module toe:
import { ActivatedRoute } from '@angular/router';
verstrek vervolgens ActivatedRoute
in de constructor van de nieuwscomponent door de volgende code toe te voegen bovenaan de klassedefinitie van NewsComponent
:
constructor(private route: ActivatedRoute) {}
Zoals u weet, of misschien al geraden had, geeft ActivatedRoute
ons toegang tot informatie over de route die op dat moment actief is, zoals de url van de route, query-parameters, enzovoort. Waar het ons hier om gaat zijn de nieuwsgegevens die in de ActivatedRoute
worden geladen door de resolve guard. Om de opgeloste nieuwsgegevens uit de route te krijgen, voegt u een eigenschap toe die de nieuwsgegevens bevat:
public news: any;
en vervolgens haalt u de gegevens uit de geactiveerde route wanneer de NewsComponent
wordt geïnitialiseerd:
ngOnInit(): void { this.news = this.route.snapshot.data; }
Dat is het zo goed als. Wijzig het sjabloon van de nieuwscomponent om het nieuws van de route op te lossen door de huidige inhoud te verwijderen:
<div>This is just a placeholder for now. News will go here. </div>
en te vervangen door:
U zou nu begroet moeten worden met het laatste nieuws, “De lucht is blauw”, wanneer u op het tabblad Nieuws klikt.
Gevolledigde code
Notes & Tips
– Resolves kunnen overbodige API-aanroepen veroorzaken: Een resolve krijgt gegevens elke keer dat een component wordt geladen. Dit resulteert vaak in onnodige API-aanroepen die de prestaties nadelig beïnvloeden. Als je resolve gegevens krijgt die niet vaak veranderen, overweeg dan om de door de resolve geretourneerde gegevens naar een eigenschap in de resolver klasse te schrijven en die eigenschap gewoon terug te sturen als ze reeds ingesteld is. Bijvoorbeeld, in ons voorbeeld zouden we een aanvankelijk ongedefinieerde news
eigenschap toevoegen zoals dit: public news: any = undefined;
in onze nieuws resolver. Dan, in de resolve()
methode, controleren of de news
eigenschap al is ingesteld en de waarde retourneren zonder een API call te maken als dat zo is, d.w.z.:
resolve(): Observable<any> { if (this.news) { return this.getSavedNews(); } else { return this.getNewsFromApi() } }
Volledig code voorbeeld hieronder. Als de observeerbare syntaxis een beetje ongebruikelijk lijkt, komt dat omdat ik RxJS 6
gebruik. Er is een goede tutorial over de wijzigingen die onlangs zijn doorgevoerd in RxJS
hier als je een opfrisser nodig hebt.
Return Saved Data, if Already Fetched from API
Natuurlijk zou je verder kunnen gaan en een tijdsperiode kunnen instellen waarin de gegevens geldig zijn door niet alleen de gegevens op te slaan, maar nog een timestamp property toe te voegen en een API call te maken als de gegevens ouder zijn dan x.
– Net als in AngularJS, kun je meerdere resolves gebruiken op dezelfde route. De resolves worden parallel uitgevoerd en de component wordt pas geladen als alle resolves gegevens hebben teruggestuurd. Om meerdere resolves te gebruiken, voeg je ze gewoon toe aan de route:
Dan kunnen extra resolve gegevens worden benaderd vanuit de route snapshot net als een enkele resolve:
ngOnInit(): void { this.news = this.route.snapshot.data; this.alternativeNews = this.route.snapshot.data; }
Code With Multiple Resolves
– De resolver heeft toegang tot route params. Stel dat je een component hebt die een lijst met titels van nieuwsberichten weergeeft. Wanneer op een verhaal wordt geklikt, wordt een andere component geopend, die het volledige artikel toont. Alvorens die component te laden, moeten we de inhoud van het nieuwsverhaal waarvan de titel werd aangeklikt pre-fetchen – dit kan gedaan worden in de resolve methode. De resolver class heeft toegang tot de ActivatedRoute
, dus we kunnen de id van het verhaal waarop geklikt is krijgen:
resolve(route: ActivatedRouteSnapshot) { let id: any = route.params); return this.getNewsStory(id); }
>Dit spreekt voor zich. Voor een voorbeeld, bekijk het nieuwe src/news-story-resolver.service.ts
bestand in de link hieronder. De links naar de nieuwe component zijn toegevoegd in het tabblad Nieuws (news.component.ts
).
Code With Parameterized Resolve