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.