Det kan være fyldt med fejl at udvikle en applikation i den virkelige verden med flere kald til serveren. Hvis du er her, betyder det, at du har kæmpet med forsinkelsen af API-kald. Disse forsinkelser kan forårsage en negativ UX. I dag skal vi forstå Route Resolvers i Angular 8. Der er flere forskellige ting, som vi kan gøre for at forbedre brugeroplevelsen, f.eks. ved at vise en fremskridtsindikator. For at holde os til emnet skal vi se, hvad en Route Resolver er, og hvad vi kan opnå med den.
Hvad er en Angular Route Resolver?
En Resolver er en klasse, der implementerer Resolve-grænsefladen i Angular Router. Resolver er faktisk en tjeneste, der skal være i rodmodulet. Dybest set fungerer en Resolver som middleware, der kan udføres, før en komponent indlæses.
Du kan også lide: Angular Resolvers.
Hvorfor bruge ruteopløsere i Angular?
For at forklare, hvordan en resolver kan bruges i Angular, skal vi tænke på det scenarie, hvor du bruger *ngIf="some condition"
, og din logik er afhængig af længden af et array, som manipuleres, når et API-opkald er afsluttet. Du kan f.eks. ønske at vise elementerne i dette array i en komponent, som netop er hentet i en uordnet liste.
<ul><li *ngFor="let item of items">{{item.description}}</li></ul>
I denne situation kan du komme ud for et problem, fordi dine data kommer frem, efter at komponenten er klar. Elementer i arrayet eksisterer ikke rigtig endnu. Her kommer Route Resolver til at være praktisk. Angular’s Route Resolver-klasse vil hente dine data, før komponenten er klar. Dine betingede udsagn vil fungere problemfrit med Resolver.
Resolve-interface
Hvor vi begynder at implementere en route resolver, skal vi først se, hvordan Resolve-interfacet ser ud.
export interface Resolve<T> { resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<T> | Promise<T> | T { return 'Data resolved here...' }}
Hvis du ønsker at oprette en Route Resolver, skal du oprette en ny klasse, der implementerer ovenstående interface. Denne grænseflade giver os en resolve
funktion, som giver dig to parametre, hvis du har brug for dem. Den første er ruten, som er af typen ActivatedRouteSnapshot
, og den anden er tilstanden, som er af typen RouterStateSnapshot
. Her kan du foretage et API-kald, der får de data, du har brug for, inden din komponent indlæses.
Ved hjælp af ruteparameteren kan du få ruteparametre, der kan bruges i API-kaldet. resolve
-metoden kan returnere en Observable
, et løfte eller blot en brugerdefineret type.
Bemærk: Det er vigtigt at nævne, at kun opløste data vil blive returneret via denne metode.
Dette er en fejl, som mange mennesker vil begå, når de bruger resolvere for første gang! Så du skal færdiggøre dem, før du sender dem i routeren.
Sending Data to the Router via a Resolver
Lad mig vise dig, hvad jeg mener i ovenstående afsnit. Lad os sige, at du har en metode, der returnerer en observabel:
items: any = ;getData(): Observable<any> { const observable = Observable.create(observer => { observer.next(this.items) }); return observable;}
Så, nu vil du se, at det abonnement, vi har, aldrig rammer. Det skyldes, at du ikke sender data korrekt. Du er ikke fuldføre abonnementet. For at rette den fejl skal du færdiggøre abonnementet ved at tilføje en linje kode mere.
observer.complete();
Hvis du returnerer et løfte, skal du løse det, før du sender dataene til routeren.
Implementering af en route-resolver
Når vi er færdige med den grundlæggende forklaring og hvorfor og hvordan man bruger en route-resolver, skal vi begynde at implementere en. I dette korte eksempel antager jeg, at du er bekendt med Angular CLI-kommandoer, og hvordan man opretter et nyt projekt, så jeg vil kun demonstrere den obligatoriske kode. Du kan finde demoprojektet via GitHub-repositoriet i slutningen af artiklen.
I dette eksempel vil jeg bruge jsonplaceholder som et falsk API til at hente brugerdata for at demonstrere API-opkald med ruteopløsere.
Først og fremmest har vi brug for en tjeneste, der henter brugerdataene for os. I denne tjeneste har vi en funktion kaldet getUsers()
, der returnerer 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 er vigtigt ikke at abonnere på funktionen getUsers. Ruteopløseren kaldet UserResolver vil tage sig af dette for dig. Det næste skridt er at oprette en ny tjeneste kaldet UserResolver, som implementerer resolve-funktionen i routerens Resolve-grænseflade.
@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(); }); ); }}
Denne tjeneste, UserResolver
, abonnerer automatisk på getUsers
observablen og forsyner routeren med de hentede data. I tilfælde af en fejl, mens du henter dataene, kan du sende en tom observable, og routeren vil ikke gå videre til ruten.
Navigationen vil blive afbrudt på dette tidspunkt. Dette sidste trin er at oprette en komponent, der vil blive kaldt, når brugeren går til /users
-ruten. Typisk skal du uden en Resolver hente dataene på komponentens ngOnInit
hook og håndtere de fejl, der opstår, fordi der ikke findes “ingen data”. Brugerens komponent er en simpel komponent. Den henter blot brugerens data fra ActivatedRoute
og viser dem i en uordnet liste.
Når du har oprettet brugerens komponent, skal du definere ruterne og fortælle routeren, at den skal bruge en resolver ( UserResolver
). Dette kan opnås med følgende kode i app-routing.modulte.ts
.
.
const routes: Routes = ;@NgModule({ imports: , exports: })export class AppRoutingModule { }
Du skal indstille resolve-egenskaben i brugerens rute og erklære UserResolver
. Dataene vil blive overført til et objekt med en egenskab kaldet users. Herefter er du næsten færdig. Der er kun én ting, du skal gøre. Du skal hente de hentede data ind i users’ komponent via ActivatedRoute
med følgende kode:
constructor(private activatedRoute: ActivatedRoute) { }users: any;ngOnInit() { this.activatedRoute.data.subscribe((data: { users: any }) => { this.users = data.users; });}
Så kan du bare vise dem i HTML uden *ngIf
-angivelser ( *ngIf="users && users.length > 0
), fordi dataene vil være der, før komponenten indlæses.
<h2>Fetched Users:</h2><ul><li *ngFor="let user of users">{{ user.name }}</li></ul>
Summary
Slutter denne artikel om Route Resolvers, vil jeg gerne endnu en gang påpege de fordele, som de giver os. For det første undgår vi de irriterende kontroller, der skal foretages på HTML-niveau, så vi ikke har problemer, før vores data er modtaget. For det andet giver de os mulighed for at fokusere mere på brugeroplevelsen, da vi kan stoppe navigationen, hvis der opstår en datafejl, mens vi henter dem, uden at vi behøver at indlæse preview-komponenten. Vi kan også gøre mere, før vi afslutter navigationen, f.eks. vedhæfte mere logik eller mappe dataene, før vi får adgang til den indlæste komponent.
Demokoden er tilgængelig på GitHub.
Videre læsning
- Angular Tutorial: Angular 7 og RESTEasy Framework.
- Opbygning af en Angular 5-applikation trin-for-trin.
- Angular 7 + Spring Boot-applikation: Hello World Eksempel.