Att utveckla en riktig applikation med flera anrop till servern kan vara fullt av fel. Om du är här betyder det att du har kämpat med fördröjningen av API-samtal. Dessa fördröjningar kan orsaka en negativ UX. Idag ska vi förstå Route Resolvers i Angular 8. Det finns flera olika saker som vi kan göra för att förbättra användarupplevelsen, till exempel att visa en förloppsindikator. För att hålla oss till ämnet ska vi se vad en Route Resolver är och vad vi kan uppnå med den.
Vad är en Angular Route Resolver?
En Resolver är en klass som implementerar Resolve-gränssnittet i Angular Router. Resolver är faktiskt en tjänst som måste finnas i rotmodulen. I grund och botten fungerar en Resolver som en middleware som kan exekveras innan en komponent laddas.
Du kanske också gillar: Angular Resolvers.
Varför använda Route Resolvers i Angular?
För att förklara hur en resolver kan användas i Angular, låt oss tänka på scenariot när du använder *ngIf="some condition"
och din logik är beroende av längden på en array, som manipuleras när ett API-samtal avslutas. Du kanske till exempel vill visa i en komponent de objekt i den här matrisen som just hämtades i en oordnad lista.
<ul><li *ngFor="let item of items">{{item.description}}</li></ul>
I den här situationen kan du hamna i ett problem eftersom dina data kommer att dyka upp efter att komponenten är klar. Elementen i matrisen existerar egentligen inte ännu. Här kommer Route Resolver väl till pass. Angulars Route Resolver-klass hämtar dina data innan komponenten är klar. Dina villkorliga påståenden kommer att fungera smidigt med Resolver.
Resolve Interface
För att börja implementera en Route Resolver ska vi först se hur Resolve-gränssnittet ser ut.
export interface Resolve<T> { resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<T> | Promise<T> | T { return 'Data resolved here...' }}
Om du vill skapa en Route Resolver måste du skapa en ny klass som kommer att implementera ovanstående gränssnitt. Det här gränssnittet ger oss en resolve
-funktion som ger dig två parametrar om du behöver dem. Den första är rutten, som är av typen ActivatedRouteSnapshot
, och den andra är tillståndet, av typen RouterStateSnapshot
. Här kan du göra ett API-anrop som hämtar de data du behöver innan din komponent laddas.
Med parametern route kan du hämta routeparametrar som kan användas i API-anropet. resolve
-metoden kan returnera en Observable
, ett löfte eller bara en anpassad typ.
Notera: Det är viktigt att nämna att endast resolverade data returneras via den här metoden.
Detta är ett misstag som många kommer att göra när de använder resolvers för första gången! Så du måste komplettera dem innan du skickar dem i routern.
Sända data till routern via en resolver
Låt mig visa dig vad jag menar i ovanstående avsnitt. Låt oss säga att du har en metod som returnerar en observabel:
items: any = ;getData(): Observable<any> { const observable = Observable.create(observer => { observer.next(this.items) }); return observable;}
Så, nu kommer du att se att den prenumeration som vi har, aldrig träffar. Detta beror på att du inte skickar data på rätt sätt. Du slutför inte prenumerationen. För att åtgärda det felet måste du slutföra prenumerationen genom att lägga till ytterligare en kodrad.
observer.complete();
Om du returnerar ett löfte måste du lösa det innan du skickar data till routern.
Implementering av en Route Resolver
När vi är färdiga med den grundläggande förklaringen och varför och hur vi ska använda en Route Resolver, ska vi börja implementera en. I det här korta exemplet antar jag att du är bekant med Angulars CLI-kommandon och hur man skapar ett nytt projekt, så jag kommer bara att demonstrera den obligatoriska koden. Du hittar demoprojektet via GitHub-förrådet i slutet av artikeln.
I det här exemplet kommer jag att använda jsonplaceholder som ett falskt API för att hämta användardata för att demonstrera API-anrop med route resolvers.
För det första behöver vi en tjänst som hämtar användardata åt oss. I den här tjänsten har vi en funktion som heter getUsers()
som returnerar en observabel.
@Injectable({providedIn: 'root'})export class FakeApiService { constructor(private http: HttpClient) { } private usersEndpoint = "https://jsonplaceholder.typicode.com/users"; getUsers(): Observable<any> { // We do not subscribe here! We let the resolver take care of that... return this.http.get(this.usersEndpoint); }}
Det är viktigt att inte prenumerera på funktionen getUsers. Ruttupplösaren som kallas UserResolver kommer att ta hand om detta åt dig. Nästa steg är att skapa en ny tjänst kallad UserResolver som implementerar resolve-funktionen i routerns Resolve-gränssnitt.
@Injectable({providedIn: 'root'})export class UserResolverService implements Resolve<any> { constructor(private fakeApi: FakeApiService) { } resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { return this.fakeApi.getUsers().pipe( catchError((error) => { return empty(); }); ); }}
Denna tjänst, UserResolver
, kommer automatiskt att prenumerera på getUsers
observable och förse routern med de hämtade uppgifterna. Om ett fel uppstår när du hämtar data kan du skicka en tom observabel och routern kommer inte att gå vidare till rutten.
Navigationen avslutas vid denna punkt. Det sista steget är att skapa en komponent som kommer att anropas när användaren går till /users
-rutten. Typiskt sett, utan en Resolver, måste du hämta data på komponentens ngOnInit
hook och hantera de fel som orsakas av att ”inga data” finns. Användarens komponent är en enkel komponent. Den hämtar bara användarens data från ActivatedRoute
och visar dem i en oordnad lista.
När du har skapat användarens komponent behöver du definiera vägarna och tala om för routern att använda en resolver ( UserResolver
). Detta kan uppnås med följande kod i app-routing.modulte.ts
.
.
const routes: Routes = ;@NgModule({ imports: , exports: })export class AppRoutingModule { }
Du måste ställa in egenskapen resolve i användarens rutt och deklarera UserResolver
. Uppgifterna kommer att skickas till ett objekt med en egenskap som heter users. Efter det är du nästan klar. Det finns bara en sak som du behöver göra. Du måste få in de hämtade uppgifterna i komponenten users via ActivatedRoute
med följande kod:
constructor(private activatedRoute: ActivatedRoute) { }users: any;ngOnInit() { this.activatedRoute.data.subscribe((data: { users: any }) => { this.users = data.users; });}
Därefter kan du bara visa dem i HTML utan *ngIf
-angivelser ( *ngIf="users && users.length > 0
) eftersom uppgifterna kommer att finnas där innan komponenten laddas.
<h2>Fetched Users:</h2><ul><li *ngFor="let user of users">{{ user.name }}</li></ul>
Sammanfattning
Som avslutning på den här artikeln om Route Resolvers vill jag än en gång påpeka de fördelar de ger oss. Först undviker vi de irriterande kontrollerna som måste göras på HTML-nivå så att vi inte har några problem förrän våra data tas emot. För det andra gör de att vi kan fokusera mer på användarupplevelsen eftersom vi kan avbryta navigeringen om ett datafel uppstår när vi hämtar dem, utan att behöva ladda förhandsvisningskomponenten. Vi kan också göra mer innan vi avslutar navigeringen, till exempel bifoga mer logik eller mappa data innan vi får tillgång till den inlästa komponenten.
Demokoden finns tillgänglig på GitHub.
Fördjupad läsning
- Angular Tutorial: Angular 7 and the RESTEasy Framework.
- Building an Angular 5 Application Step-By-Step.
- Angular 7 + Spring Boot Application: Hello World Exempel.