Opdateret den 13/07/2018: Omfattende overhaling af koden for at bruge nyere konventioner. Opgraderet Angular og RxJS til de nyeste tilgængelige versioner, flyttet eksempler til StackBlitz.
Målet med dette indlæg er at demonstrere, hvordan Angular route resolves fungerer på 10 minutter, sammen med at give eksempler på kode på Stackblitz. Hvis du allerede er bekendt med route resolves fra AngularJS, anbefaler jeg, at du springer introen over og hopper direkte til afsnittet med prøveappen.
Indholdsfortegnelse
Intro
Sample App
- Step 1: Opret en Resolver-klasse
- Step 2: Tilføj en Resolve Guard til ruten
- Step 3: Hent de opløste data fra komponentens aktiverede rute
Notes & Tips
- Undgå overflødige API-kald
- Multiple Resolves on den samme rute
- Forsendelse af ruteparametre til opløseren
Sluttende noter
Intro
Ruteopløsninger er intet andet end en måde at præ-hente de data, som en komponent har brug for, før den initialiseres. Normalt kommer disse data fra en API. Lad os sige, at du har en komponent, hvis eneste rolle er at vise et diagram over det daglige salg for måneden; Der er ingen mening med at rendere visningen eller indlæse denne komponent, før salgsdataene er tilgængelige. Faktisk vil mange diagrambiblioteker give en fejl, hvis du forsøger at initialisere et diagram, før du forsyner det med data (og det vil *ngFor
også gøre). Du kan naturligvis nemt omgå dette problem ved at skjule html’en med en *ngIf
eller ved midlertidigt at levere et tomt array, indtil de nødvendige data er indlæst. Men selv om du kan klare dig uden resolves, hjælper implementeringen af dem med at gøre koden mere læsbar og vedligeholdelsesvenlig ved at:
- Eliminere forvirrende rod i din komponents skabelon og kode.
- Klargør din hensigt ved at vise, hvilke data der skal pre-fetches.
Trods disse fordele undgår mange websteder at bruge resolves til fordel for at vise det meste af komponenten og vise en spinner i de sektioner, hvor data stadig skal indlæses. I nogle tilfælde er denne fremgangsmåde ønskværdig ud fra et UX-synspunkt, og en resolve bør ikke anvendes. Brug dit skøn.
Sample App
Åbn Sample App
Som du kan se, er vores udgangspunkt en meget grundlæggende app med to ruter – “Home” og “News”. Du navigerer mellem ruterne ved at klikke på den tilsvarende fane. Vi vil tilføje en resolve til at pre-fetch nyheder fra et API, før News-komponenten indlæses. Dette vil tage tre trin:
Stræk 1: Opret en resolver-klasse, som foretager et Http-opkald for at pre-fetche de data, vi har brug for.
Opret en ny Typescript-fil i mappen app
og navngiv den:
news-resolver.service.ts
Kopier derefter følgende kode og indsæt den i din nye fil (forklaret nedenfor):
Lad os opdele, hvad vi gjorde i koden ovenfor:
-
Føjede ES6-import-statements til for at bringe de nødvendige moduler ind.
-
Opret en ny TypeScript
NewsResolver
-klasse. -
Føjede
Resolve
-grænsefladen til vores klasse – dette er VALGFRI, men enhver klasse, vi planlægger at bruge som resolver, skal implementere en resolve-metode, så det er en god konvention. -
Føjede en
resolve()
-metode tilNewsResolver
– dette er den metode, der er ansvarlig for at returnere de data, vi har brug for. Det er IKKE valgfrit at navngive den metode, der returnerer data til en resolve-vagt, “resolve” – resolveren ville ikke fungere, hvis metoden hed noget andet. -
Hvis du har bemærket, at der fejlagtigt anvendes en
POST
-anmodning i stedet for enGET
i ovenstående kode, har du helt ret; I en rigtig app ville det være enGET
-anmodning. Eksemplerne her udnytter, som er et websted, der leverer test-API-endpoints.
Hvor vi går videre, skal vi inkludere den resolver-klasse, vi lige har oprettet, i vores routing-modul. Naviger til app-routing.module.ts
, og tilføj NewsResolver
til arrayet providers
. Eller, hvis du lige er begyndt at arbejde med Angular 2, skal du blot erstatte indholdet af app-routing.module.ts
med nedenstående kode – ændringerne er markeret med noter:
Med dette gjort, har vi nu defineret resolver-klassen. I de følgende trin tilføjer vi den til vores rute.
Kode efter tilføjelse af resolver-klassen
Stræk 2: Tilføj en resolve-vagt til ruten.
I app-routing.module.ts
ændres følgende kodelinje { path: 'news', component: NewsComponent }
til:
{ path: 'news', component: NewsComponent, resolve: { news: NewsResolver }}
Det eneste, vi gjorde her, var at tilføje den resolve-vagt, vi netop har defineret, til vores news
-rute. Dette fortæller Angular, at vi skal vente på, at NewsResolver
s resolve()
-metode resolve()
returnerer data, før vi viser NewsComponent
.
Det er vigtigt at påpege, at news
i kodelinje news: NewsResolver
er det, jeg valgte at navngive de data, der returneres af resolveren. Du kan navngive det som du vil.
Som en lidt tangentiel bemærkning – hvis du ikke er bekendt med Angular route guards generelt, og gerne vil vide mere, er dokumentationen her. At gå i detaljer om guards ligger uden for rammerne af dette indlæg, men du bør vide, at der er andre guards end resolve
til rådighed, hvilket er grunden til, at jeg bragte begrebet på banen.
Kode med Resolve Guard tilføjet
Stræk 3: Hent de resolverede data fra komponentens aktiverede rute.
Nu da vi har en resolver-klasse, og vi har tilføjet den til vores rute, bliver data hentet på forhånd. Det sidste trin er at få adgang til de præ-fetchede data i vores NewsComponent
, der er placeret i app/news.component.ts
. Naviger til denne fil, og tilføj følgende ES6-modul:
import { ActivatedRoute } from '@angular/router';
tilvejebring derefter ActivatedRoute
i News-komponentens konstruktør ved at tilføje følgende kode øverst i NewsComponent
-klassedefinitionen:
constructor(private route: ActivatedRoute) {}
Som du ved, eller måske har gættet, giver ActivatedRoute
os adgang til oplysninger om den rute, der er aktiv i øjeblikket, såsom ruteens url, forespørgselsparametre osv. Det, vi interesserer os for her, er de nyhedsdata, som bliver indlæst i ActivatedRoute
fra resolve-vagten. Hvis du vil hente de resolved nyhedsdata fra ruten, skal du tilføje en egenskab til at holde nyhedsdataene:
public news: any;
og derefter hente dataene fra den aktiverede rute, når NewsComponent
initialiseres:
ngOnInit(): void { this.news = this.route.snapshot.data; }
Det er stort set det hele. Ændr skabelonen for nyhedskomponenten, så den viser nyhederne fra ruten resolve ved at fjerne det nuværende indhold:
<div>This is just a placeholder for now. News will go here. </div>
og erstatte det med:
Du skulle nu blive mødt med den seneste nyhed, “The sky is blue”, når du klikker på fanen Nyheder.
Færdiggjort kode
Noter & Tips
– Løsninger kan forårsage overflødige API-kald: En resolve får data, hver gang en komponent indlæses. Dette resulterer ofte i unødvendige API-kald, som påvirker ydeevnen negativt. Hvis din resolve får data, der ikke ændres ofte, kan du overveje at skrive de data, der returneres af resolve’en, til en egenskab i resolver-klassen og blot returnere denne egenskab, hvis den allerede er indstillet. I vores eksempel ville vi f.eks. tilføje en oprindeligt udefineret news
-egenskab på følgende måde: public news: any = undefined;
i vores nyhedsopløser. I resolve()
-metoden kontrollerer vi derefter, om news
-egenskaben allerede er indstillet, og returnerer dens værdi uden at foretage et API-opkald, hvis den er indstillet, dvs.:
resolve(): Observable<any> { if (this.news) { return this.getSavedNews(); } else { return this.getNewsFromApi() } }
Komplet kodeeksempel nedenfor. Hvis den observerbare syntaks virker lidt usædvanlig for dig, er det fordi jeg bruger RxJS 6
. Der er en god vejledning om de ændringer, der for nylig blev indført i RxJS
her, hvis du har brug for en genopfriskning.
Return Saved Data, if Already Fetched from API
Naturligvis kan du gå videre og indstille en tidsperiode, hvor dataene er gyldige, ved ikke kun at gemme dataene, men tilføje en anden timestamp-egenskab og foretage et API-opkald, hvis dataene er ældre end x.
– Ligesom i AngularJS kan du bruge flere resolves på den samme rute. Resolve-opkaldene foretages parallelt, og komponenten vil først blive indlæst, når alle opkaldene returnerer data. Hvis du vil bruge flere resolves, skal du blot tilføje dem til ruten:
Så kan du få adgang til yderligere resolve-data fra rute-snapshotet ligesom med en enkelt resolve:
ngOnInit(): void { this.news = this.route.snapshot.data; this.alternativeNews = this.route.snapshot.data; }
Kode med flere resolves
– Resolveren har adgang til ruteparamerne. Lad os sige, at du har en komponent, der viser en liste over titler på nyhedshistorier. Når der klikkes på en historie, åbnes en anden komponent, der viser den fulde artikel. Før vi indlæser denne komponent, skal vi på forhånd hente indholdet af den nyhedshistorie, hvis titel der blev klikket på – dette kan gøres i resolve-metoden. Resolver-klassen har adgang til ActivatedRoute
, så vi kan få id’et for den historie, der blev klikket på:
resolve(route: ActivatedRouteSnapshot) { let id: any = route.params); return this.getNewsStory(id); }
Dette er ret selvforklarende. For at få et eksempel kan du se den nye src/news-story-resolver.service.ts
-fil i linket nedenfor. Links til den nye komponent blev tilføjet i fanen Nyheder (news.component.ts
).
Kode med parameteriseret opløsning