Az Angular útvonalfeloldók megértése

winding-road-between-forest

Egy valós alkalmazás fejlesztése, amely többször hívja a szervert, tele lehet hibákkal. Ha itt vagy, az azt jelenti, hogy küzdöttél már az API-hívások késleltetésével. Ezek a késések negatív UX-et okozhatnak. Ma meg fogjuk érteni a Route Resolvereket az Angular 8-ban. Számos különböző dolgot tehetünk a felhasználói élmény javítására, például egy előrehaladásjelző megjelenítését. Hogy a témánál maradjunk, nézzük meg, mi az a Route Resolver és mit érhetünk el vele.

Mi az Angular Route Resolver?

A Resolver egy olyan osztály, amely az Angular Router Resolve interfészét valósítja meg. Valójában a Resolver egy olyan szolgáltatás, amelynek a gyökérmodulban kell lennie. Alapvetően egy Resolver úgy viselkedik, mint egy middleware, ami egy komponens betöltése előtt futtatható.

You may also like: Angular Resolverek.

Miért használjunk Route Resolvert az Angularban?

Hogy elmagyarázzuk, hogyan használható egy Resolver az Angularban, gondoljunk arra a forgatókönyvre, amikor a *ngIf="some condition"-t használjuk, és a logikánk egy tömb hosszára támaszkodik, amelyet egy API hívás befejezésekor manipulálunk. Például egy komponensben szeretnénk megjeleníteni ennek a tömbnek az éppen lehívott elemeit egy rendezetlen listában.

<ul><li *ngFor="let item of items">{{item.description}}</li></ul>

Ebben a helyzetben problémába kerülhetünk, mert az adatok a komponens elkészülte után fognak előjönni. A tömbben lévő elemek még nem igazán léteznek. Itt jön jól a Route Resolver. Az Angular Route Resolver osztálya le fogja hívni az adataidat, mielőtt a komponens elkészülne. A feltételes utasításaink zökkenőmentesen fognak működni a Resolver segítségével.

Resolve Interface

Mielőtt elkezdenénk egy Route Resolver implementálását, nézzük meg először, hogyan néz ki a Resolve interfész.

export interface Resolve<T> { resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<T> | Promise<T> | T { return 'Data resolved here...' }}

Ha Route Resolvert szeretnénk létrehozni, akkor egy új osztályt kell létrehoznunk, amely a fenti interfészt implementálja. Ez az interfész biztosít számunkra egy resolve függvényt, amely két paramétert kap, ha szükségünk van rájuk. Az első az útvonal, amely ActivatedRouteSnapshot típusú, a második pedig az állapot, amely RouterStateSnapshot típusú. Itt olyan API-hívást végezhet, amely a komponens betöltése előtt megkapja a szükséges adatokat.

A route paraméteren keresztül megkaphatja az API-hívásban felhasználható route-paramétereket. A resolve módszer visszaadhat egy Observable, egy ígéretet vagy csak egy egyéni típust.

Figyelem: Fontos megemlíteni, hogy ezen a módszeren keresztül csak a felbontott adatok kerülnek visszaadásra.

Ezt a hibát sokan elkövetik, amikor először használnak resolvereket! Ezért ki kell egészítenie őket, mielőtt elküldi őket a routerbe.

Adatok küldése a routerbe egy reszolveren keresztül

Hadd mutassam meg, mire gondolok a fenti szakaszban. Tegyük fel, hogy van egy metódusod, amely egy megfigyelhetőt ad vissza:

items: any = ;getData(): Observable<any> { const observable = Observable.create(observer => { observer.next(this.items) }); return observable;}

Így most látni fogod, hogy a feliratkozás, amink van, soha nem talál. Ezt az okozza, hogy nem megfelelően küldöd az adatokat. Nem fejezed be a feliratkozást. Ahhoz, hogy kijavítsuk ezt a hibát, még egy sor kód hozzáadásával ki kell egészítenünk az előfizetést.

observer.complete();

Ha egy ígéretet adunk vissza, akkor azt fel kell oldanunk, mielőtt elküldjük az adatokat az útválasztónak.

A Route Resolver implementálása

Miután végeztünk az alapok magyarázatával és azzal, hogy miért és hogyan kell használni egy route resolvert, kezdjük el implementálni azt. Ebben a rövid példában feltételezem, hogy ismered az Angular CLI parancsait és az új projekt létrehozását, ezért csak a kötelező kódot fogom bemutatni. A demóprojektet a cikk végén található GitHub tárolón keresztül találod meg.

Ebben a példában a jsonplaceholder-t fogom használni, mint hamis API-t a felhasználói adatok lehívására, hogy bemutassam az API-hívásokat az útvonalfeloldókkal.

Először is szükségünk lesz egy szolgáltatásra, amely lekérdezi nekünk a felhasználói adatokat. Ebben a szolgáltatásban van egy getUsers() nevű függvényünk, amely egy megfigyelhetőt ad vissza.

@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); }}

Nem fontos, hogy feliratkozzunk a getUsers függvényre. Erről a UserResolver nevű útvonalfeloldó gondoskodik helyettünk. A következő lépés egy új UserResolver nevű szolgáltatás létrehozása, amely az útválasztó Resolve interfészének resolve függvényét fogja megvalósítani.

@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(); }); ); }}

Ez a szolgáltatás UserResolver automatikusan feliratkozik a getUsers observable-re, és biztosítja az útválasztó számára a lehívott adatokat. Hiba esetén, az adatok lehívása közben, küldhet egy üres observable-t, és az útválasztó nem folytatja az útvonalat.

A navigáció ezen a ponton megszakad. Ez az utolsó lépés egy olyan komponens létrehozása, amely meghívásra kerül, amikor a felhasználó a /users útvonalra lép. Általában Resolver nélkül a komponens ngOnInit kampóján kell lekérni az adatokat, és kezelni a “nincs adat” létezéséből adódó hibákat. A felhasználói komponens egyszerű. Csak lekérdezi a felhasználó adatait a ActivatedRoute-ból, és egy rendezetlen listában jeleníti meg őket.

A felhasználói komponens létrehozása után meg kell határoznia az útvonalakat, és meg kell mondania a router-nek, hogy használjon egy reszolvert ( UserResolver). Ezt a következő kóddal lehet elérni a app-routing.modulte.ts-ban.

const routes: Routes = ;@NgModule({ imports: , exports: })export class AppRoutingModule { }

A felhasználói útvonalba be kell állítani a resolve tulajdonságot, és deklarálni kell a UserResolver. Az adatok egy users nevű tulajdonsággal rendelkező objektumba kerülnek. Ezek után már majdnem kész vagy. Már csak egy dolgot kell tenned. A lehívott adatokat a következő kóddal kell a ActivatedRoute-on keresztül a users komponensbe juttatnia.

constructor(private activatedRoute: ActivatedRoute) { }users: any;ngOnInit() { this.activatedRoute.data.subscribe((data: { users: any }) => { this.users = data.users; });}

Aztán már csak meg kell jelenítenie őket a HTML-ben minden *ngIf utasítás nélkül ( *ngIf="users && users.length > 0 ), mert az adatok már a komponens betöltése előtt ott lesznek.

<h2>Fetched Users:</h2><ul><li *ngFor="let user of users">{{ user.name }}</li></ul>

Összefoglaló

A Route Resolverekről szóló cikk lezárásaként még egyszer szeretném kiemelni az általuk nyújtott előnyöket. Először is elkerüljük a bosszantó ellenőrzéseket, amelyeket a HTML szintjén kell elvégezni, így nem lesznek problémáink az adataink beérkezéséig. Másodszor, lehetővé teszik számunkra, hogy jobban összpontosítsunk a felhasználói élményre, mivel leállíthatjuk a navigációt, ha az adatok lekérdezése közben adathiba lép fel, anélkül, hogy be kellene töltenünk az előnézeti komponenst. A navigáció befejezése előtt is többet tehetünk, például több logikát csatolhatunk vagy leképezhetjük az adatokat, mielőtt hozzáférnénk a betöltött komponenshez.

A demókód elérhető a GitHubon.

További olvasmányok

  • Angular Tutorial: Angular 7 és a RESTEasy keretrendszer.
  • Angular 5 alkalmazás építése lépésről lépésre.
  • Angular 7 + Spring Boot alkalmazás: Hello World Example.

Szólj hozzá!