Angular Route löser sig på 10 minuter

Uppdaterad 2018-07-13: Omfattande översyn av koden för att använda nyare konventioner. Uppgraderade Angular och RxJS till de senaste tillgängliga versionerna, flyttade exempel till StackBlitz.

Målet med det här inlägget är att visa hur Angular route resolves fungerar på 10 minuter, tillsammans med att tillhandahålla exempelkod på Stackblitz. Om du redan är bekant med route resolves från AngularJS rekommenderar jag att du hoppar över introduktionen och hoppar direkt till avsnittet med exempelappen.

Innehållsförteckning

Intro
Exempelapp

  • Steg 1: Skapa en Resolver-klass
  • Steg 2: Lägg till en Resolve Guard till rutten
  • Steg 3: Hämta de upplösta uppgifterna från komponentens aktiverade rutt

Notiser & Tips

  • Undervik överflödiga API-anrop
  • Multipla upplösningar på samma rutt
  • Förmedling av ruttparametrar till resolveraren

Slutanmärkningar

Intro

Ruttresolver är inget annat än ett sätt att pre-hämta de data som en komponent behöver innan den initieras. Vanligtvis kommer dessa data från ett API. Låt oss säga att du har en komponent vars enda roll är att visa ett diagram över den dagliga försäljningen för månaden; Det finns ingen mening med att rendera vyn eller ladda den här komponenten innan försäljningsdata är tillgängliga. Faktum är att många diagrambibliotek kommer att ge ett fel om du försöker initialisera ett diagram innan du har försett det med data (och det gör även *ngFor). Naturligtvis kan du enkelt kringgå det här problemet genom att dölja html-filen med en *ngIf eller genom att tillfälligt tillhandahålla en tom array tills de nödvändiga uppgifterna laddas. Även om du kan klara dig utan resolves hjälper implementeringen av dem dock till att göra koden mer läsbar och underhållbar genom att:

  1. Eliminera förvirrande röran i din komponents mall och kod.
  2. Förtydligar din avsikt genom att visa vilka data som måste hämtas i förväg.

Trots dessa fördelar undviker många webbplatser att använda resolves till förmån för att visa större delen av komponenten och visa en spinner i de sektioner för vilka data fortfarande laddas. I vissa fall är detta tillvägagångssätt önskvärt ur UX-synpunkt, och en resolve bör inte användas. Använd ditt omdöme.

Exempelapp

Öppna exempelapp

Som du kan se är vår utgångspunkt en mycket grundläggande app med två vägar – ”Home” och ”News”. Du navigerar mellan rutterna genom att klicka på motsvarande flik. Vi ska lägga till en resolve för att förhämta nyheter från ett API innan News-komponenten laddas. Detta kommer att ske i tre steg:

Steg 1: Skapa en resolver-klass som gör ett Http-anrop för att i förväg hämta de data vi behöver.

Skapa en ny Typescript-fil i mappen app och namnge den:

news-resolver.service.ts

Kopiera sedan följande kod och klistra in den i din nya fil (förklaras nedan):

Låt oss dela upp vad vi gjorde i koden ovan:

  • Lägg till ES6-import-angivelser för att få in de nödvändiga modulerna.

  • Skapade en ny TypeScript NewsResolver-klass.

  • Lagt till Resolve-gränssnittet till vår klass – detta är FRIVILLIGT, men alla klasser vi planerar att använda som resolver måste implementera en resolve-metod, så det är en bra konvention.

  • Lagt till en resolve()-metod till NewsResolver – detta är den metod som ansvarar för att returnera de data vi behöver. Att namnge metoden som returnerar data för en resolve guard ”resolve” är INTE valfritt – resolvern skulle inte fungera om metoden hette något annat.

  • Om du har lagt märke till att en POST-förfrågan används felaktigt i stället för en GET i koden ovan har du helt rätt; i en riktig app skulle detta vara en GET-förfrågan. Exemplen här drar nytta av , som är en webbplats som tillhandahåller test-API-slutpunkter.

För att gå vidare måste vi inkludera resolver-klassen som vi just skapat i vår routningsmodul. Navigera till app-routing.module.ts och lägg till NewsResolver i arrayen providers. Eller, om du precis har börjat arbeta med Angular 2, ersätt helt enkelt innehållet i app-routing.module.ts med koden nedan – ändringarna är markerade med anteckningar:

Med detta gjort har vi nu definierat resolver-klassen. I följande steg kommer vi att lägga till den i vår rutt.

Kod efter att ha lagt till resolver-klassen

Steg 2: Lägg till en resolve-vakt till rutten.

I app-routing.module.ts ändrar du följande kodrad { path: 'news', component: NewsComponent } till:

{ path: 'news', component: NewsComponent, resolve: { news: NewsResolver }}

Allt vi gjorde här var att lägga till resolve-vakten som vi just definierade i vår news-rutt. Detta talar om för Angular att vi måste vänta på att NewsResolvers resolve()-metod returnerar data innan vi visar NewsComponent.

Det är viktigt att påpeka att news i raden news: NewsResolver i koden är vad jag valde att namnge de data som returneras av resolvern. Du kan kalla det vad du vill.

Som en lite tangentiell anmärkning – om du är obekant med Angular route guards i allmänhet, och vill veta mer, finns dokumentationen här. Att gå in på detaljer om guards ligger utanför det här inläggets räckvidd, men du bör veta att det finns andra guards än resolve tillgängliga, vilket är anledningen till att jag tog upp termen.

Kod med Resolve Guard tillagd

Steg 3: Hämta den upplösta datan från komponentens aktiverade rutt.

Nu när vi har en resolver-klass och har lagt till den i vår rutt, hämtas data på förhand. Det sista steget är att få tillgång till de förinställda uppgifterna i vår NewsComponent som ligger i app/news.component.ts. Navigera till den filen och lägg till följande ES6-modul:

import { ActivatedRoute } from '@angular/router';

tillhandahåll sedan ActivatedRoute i nyhetskomponentens konstruktör genom att lägga till följande kod högst upp i NewsComponent-klassdefinitionen:

constructor(private route: ActivatedRoute) {}

Som du vet, eller kanske har gissat, kan vi med ActivatedRoute få åtkomst till information om den rutt som för tillfället är aktiv, t.ex. ruttens url, frågeparametrar osv. Det vi bryr oss om här är de nyhetsdata som laddas in i ActivatedRoute från resolve guard. För att hämta de resolverade nyhetsdata från rutten lägger du till en egenskap för att hålla nyhetsdata:

public news: any;

och hämtar sedan data från den aktiverade rutten när NewsComponent initialiseras:

 ngOnInit(): void { this.news = this.route.snapshot.data; }

Det är praktiskt taget allt. Ändra mallen för nyhetskomponenten så att den visar nyheterna från rutten resolve genom att ta bort det nuvarande innehållet:

<div>This is just a placeholder for now. News will go here. </div>

och ersätta det med:

Du bör nu mötas av den senaste nyheten, ”Himlen är blå”, när du klickar på fliken Nyheter.

Färdig kod

Anteckningar & Tips

– Upplösningar kan orsaka överflödiga API-anrop: En resolve hämtar data varje gång en komponent laddas. Detta leder ofta till onödiga API-anrop som påverkar prestandan negativt. Om din resolve får data som inte ändras ofta kan du överväga att skriva de data som returneras av resolve till en egenskap i resolver-klassen och helt enkelt returnera den egenskapen om den redan har ställts in. I vårt exempel skulle vi till exempel lägga till en initialt odefinierad news-egenskap på följande sätt: public news: any = undefined; i vår nyhetsupplösare. I metoden resolve() kontrollerar vi sedan om egenskapen news redan är inställd och returnerar dess värde utan att göra ett API-samtal om så är fallet, det vill säga:

 resolve(): Observable<any> { if (this.news) { return this.getSavedNews(); } else { return this.getNewsFromApi() } }

Komplett kodexempel nedan. Om syntaxen för observable verkar lite ovanlig för dig beror det på att jag använder RxJS 6. Det finns en bra handledning om de ändringar som nyligen infördes i RxJS här om du behöver en repetition.

Return Saved Data, if Already Fetched from API

Naturligtvis kan du gå längre och ställa in en tidsperiod under vilken data är giltiga genom att inte bara spara data, utan även lägga till en annan timestamp-egenskap och göra ett API-samtal om data är äldre än x.

– Precis som i AngularJS kan du använda flera resolves på samma väg. Resolve-anropen görs parallellt och komponenten laddas först när alla anropen returnerar data. Om du vill använda flera resolves lägger du helt enkelt till dem i rutten:

Därefter kan ytterligare resolvedata nås från ruttens ögonblicksbild precis som en enda resolve:

 ngOnInit(): void { this.news = this.route.snapshot.data; this.alternativeNews = this.route.snapshot.data; }

Kod med flera resolves

– Resolvern har tillgång till route params. Låt oss säga att du har en komponent som visar en lista med titlar på nyhetsartiklar. När man klickar på en artikel öppnas en annan komponent som visar hela artikeln. Innan vi laddar den komponenten måste vi i förväg hämta innehållet i den nyhetsartikel vars titel klickades – detta kan göras i resolve-metoden. Resolver-klassen har tillgång till ActivatedRoute, så vi kan få fram id för den nyhet som klickades:

 resolve(route: ActivatedRouteSnapshot) { let id: any = route.params); return this.getNewsStory(id); }

Detta är ganska självförklarande. För ett exempel kan du titta på den nya src/news-story-resolver.service.ts-filen i länken nedan. Länkarna till den nya komponenten har lagts till på fliken Nyheter (news.component.ts).

Code With Parameterized Resolve

Lämna ett svar

Din e-postadress kommer inte publiceras.