Dezvoltarea unei aplicații din lumea reală cu apeluri multiple către server poate fi plină de erori. Dacă vă aflați aici, înseamnă că v-ați luptat cu întârzierea apelurilor API. Aceste întârzieri pot cauza un UX negativ. Astăzi, vom înțelege Route Resolvers în Angular 8. Există mai multe lucruri diferite pe care le putem face pentru a îmbunătăți experiența utilizatorului, cum ar fi afișarea unui indicator de progres. Pentru a rămâne la subiect, haideți să vedem ce este un Route Resolver și ce putem realiza cu el.
Ce este un Angular Route Resolver?
Un Resolver este o clasă care implementează interfața Resolve a Angular Router. De fapt, Resolver este un serviciu care trebuie să se afle în modulul rădăcină. Practic, un Resolver acționează ca un middleware, care poate fi executat înainte ca o componentă să fie încărcată.
You may also like: Angular Resolvers.
De ce să folosiți Route Resolvers în Angular?
Pentru a explica cum poate fi folosit un resolver în Angular, să ne gândim la scenariul în care folosiți *ngIf="some condition"
, iar logica dvs. se bazează pe lungimea unui array, care este manipulată atunci când se finalizează un apel API. De exemplu, s-ar putea să doriți să afișați într-o componentă elementele acestui array care tocmai au fost preluate într-o listă neordonată.
<ul><li *ngFor="let item of items">{{item.description}}</li></ul>
În această situație, s-ar putea să vă confruntați cu o problemă, deoarece datele dvs. vor apărea după ce componenta este gata. Elementele din matrice nu există cu adevărat încă. Aici, Route Resolver vine la îndemână. Clasa Route Resolver din Angular va prelua datele dvs. înainte ca componenta să fie gata. Instrucțiunile dvs. condiționale vor funcționa fără probleme cu Resolver.
Interfața Resolve
Înainte de a începe să implementăm un Route Resolver, să vedem mai întâi cum arată interfața Resolve.
export interface Resolve<T> { resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<T> | Promise<T> | T { return 'Data resolved here...' }}
Dacă doriți să creați un Route Resolver, trebuie să creați o clasă nouă care va implementa interfața de mai sus. Această interfață ne pune la dispoziție o funcție resolve
, care vă obține doi parametri în cazul în care aveți nevoie de ei. Primul este ruta, care este de tip ActivatedRouteSnapshot
, iar cel de-al doilea este starea, de tip RouterStateSnapshot
. Aici, puteți face un apel API care va obține datele de care aveți nevoie înainte de încărcarea componentei dumneavoastră.
Prin intermediul parametrului de rută, puteți obține parametrii de rută care pot fi utilizați în apelul API. Metoda resolve
poate returna un Observable
, o promisiune sau doar un tip personalizat.
Nota: Este important de menționat că prin această metodă vor fi returnate doar date rezolvate.
Aceasta este o greșeală pe care mulți oameni o vor face atunci când folosesc rezolvatoare pentru prima dată! Așadar, trebuie să le completați înainte de a le trimite în router.
Întoarcerea datelor în router prin intermediul unui resolver
Dați-mi voie să vă arăt la ce mă refer în secțiunea de mai sus. Să spunem că aveți o metodă care returnează un observabil:
items: any = ;getData(): Observable<any> { const observable = Observable.create(observer => { observer.next(this.items) }); return observable;}
Acum, acum, veți vedea că abonamentul pe care îl avem, nu lovește niciodată. Acest lucru este cauzat de faptul că nu trimiteți datele corect. Nu completați abonamentul. Pentru a remedia această eroare, trebuie să completați abonamentul adăugând încă o linie de cod.
observer.complete();
În cazul în care returnați o promisiune, trebuie să o rezolvați înainte de a trimite datele către router.
Implementarea unui Route Resolver
După ce am terminat cu explicațiile de bază și de ce și cum să folosim un Route Resolver, haideți să începem să implementăm unul. În acest scurt exemplu, presupun că sunteți familiarizați cu comenzile CLI de la Angular și cu modul de creare a unui nou proiect, așa că voi demonstra doar codul obligatoriu. Puteți găsi proiectul demonstrativ prin intermediul depozitului GitHub de la sfârșitul articolului.
În acest exemplu, voi folosi jsonplaceholder ca un API fals pentru a prelua datele utilizatorului pentru a demonstra apelurile API cu route resolvere.
În primul rând, vom avea nevoie de un serviciu care va prelua datele utilizatorului pentru noi. În acest serviciu, avem o funcție numită getUsers()
care returnează un observabil.
@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); }}
Este important să nu ne abonăm la funcția getUsers. Rezolvatorul de rute numit UserResolver se va ocupa de acest lucru pentru dumneavoastră. Următorul pas este crearea unui nou serviciu numit UserResolver care va implementa funcția de rezolvare a interfeței Resolve a routerului.
@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(); }); ); }}
Acest serviciu, UserResolver
, se va abona automat la observabilul getUsers
și va furniza routerului datele preluate. În cazul unei erori, în timpul preluării datelor, puteți trimite un observabil gol, iar routerul nu va continua traseul.
Navigația se va încheia în acest punct. Acest ultim pas constă în crearea unei componente care va fi apelată atunci când utilizatorul merge la ruta /users
. În mod obișnuit, fără un Resolver, va trebui să preluați datele pe cârligul ngOnInit
al componentei și să gestionați erorile cauzate de existența „no data”. Componenta utilizatorului este una simplă. Ea nu face decât să obțină datele utilizatorului din ActivatedRoute
și să le afișeze într-o listă neordonată.
După ce ați creat componenta utilizatorului, trebuie să definiți rutele și să spuneți routerului să folosească un rezolutor ( UserResolver
). Acest lucru ar putea fi realizat cu următorul cod în componenta app-routing.modulte.ts
.
const routes: Routes = ;@NgModule({ imports: , exports: })export class AppRoutingModule { }
Trebuie să setați proprietatea resolve în ruta utilizatorului și să declarați UserResolver
. Datele vor fi trecute într-un obiect cu o proprietate numită users. După aceasta, sunteți aproape gata. Mai este un singur lucru pe care trebuie să îl faceți. Trebuie să introduceți datele preluate în componenta users prin intermediul ActivatedRoute
cu următorul cod.
constructor(private activatedRoute: ActivatedRoute) { }users: any;ngOnInit() { this.activatedRoute.data.subscribe((data: { users: any }) => { this.users = data.users; });}
Apoi, puteți doar să le afișați în HTML fără declarații *ngIf
( *ngIf="users && users.length > 0
), deoarece datele vor fi acolo înainte de a fi încărcată componenta.
<h2>Fetched Users:</h2><ul><li *ngFor="let user of users">{{ user.name }}</li></ul>
Rezumat
Încheind acest articol despre Route Resolvers, aș dori să subliniez încă o dată avantajele pe care acestea ni le oferă. În primul rând, evităm verificările enervante care trebuie făcute la nivel HTML, astfel încât să nu avem probleme până la primirea datelor noastre. În al doilea rând, acestea ne permit să ne concentrăm mai mult pe experiența utilizatorului, deoarece putem opri navigarea dacă apare o eroare de date în timpul preluării acestora, fără a fi nevoie să încărcăm componenta de previzualizare. De asemenea, putem face mai multe înainte de a finaliza navigarea, cum ar fi atașarea mai multă logică sau cartografierea datelor înainte de a accesa componenta încărcată.
Codul demo este disponibil pe GitHub.
Lecturi suplimentare
- Tutorial Angular: Angular 7 and the RESTEasy Framework.
- Construirea unei aplicații Angular 5 pas cu pas.
- Angular 7 + Spring Boot Application: Exemplu Hello World.