Understanding Angular Route Resolvers

winding-road-between-forest

Sviluppare un’applicazione del mondo reale con più chiamate al server può essere pieno di bug. Se siete qui, significa che avete lottato con il ritardo delle chiamate API. Questi ritardi possono causare una UX negativa. Oggi, andremo a capire i Route Resolver in Angular 8. Ci sono diverse cose che possiamo fare per migliorare l’esperienza dell’utente, come la visualizzazione di un indicatore di progresso. Per rimanere in tema, vediamo cos’è un Route Resolver e cosa possiamo ottenere con esso.

Cos’è un Angular Route Resolver?

Un Resolver è una classe che implementa l’interfaccia Resolve di Angular Router. In effetti, Resolver è un servizio che deve essere nel modulo root. Fondamentalmente, un Resolver agisce come un middleware, che può essere eseguito prima che un componente sia caricato.

Ti potrebbe anche piacere: Resolver angolari.

Perché usare i Route Resolver in Angular?

Per spiegare come un resolver può essere usato in Angular, pensiamo allo scenario in cui state usando *ngIf="some condition", e la vostra logica si basa sulla lunghezza di un array, che viene manipolato quando una chiamata API viene completata. Per esempio, potreste voler visualizzare in un componente gli elementi di questo array che sono stati appena recuperati in una lista non ordinata.

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

In questa situazione, potreste avere un problema perché i vostri dati verranno fuori dopo che il componente è pronto. Gli elementi nell’array non esistono ancora realmente. Qui, il Route Resolver torna utile. La classe Route Resolver di Angular recupererà i vostri dati prima che il componente sia pronto. Le vostre dichiarazioni condizionali funzioneranno senza problemi con il Resolver.

Interfaccia Resolve

Prima di iniziare ad implementare un route resolver, vediamo prima come appare l’interfaccia Resolve.

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

Se volete creare un Route Resolver, dovete creare una nuova classe che implementerà l’interfaccia di cui sopra. Questa interfaccia ci fornisce una funzione resolve, che ci procura due parametri nel caso in cui ne abbiate bisogno. Il primo è la rotta, che è di tipo ActivatedRouteSnapshot, e il secondo è lo stato, di tipo RouterStateSnapshot. Qui, puoi fare una chiamata API che otterrà i dati di cui hai bisogno prima che il tuo componente sia caricato.

Dal parametro route, puoi ottenere i parametri del percorso che possono essere usati nella chiamata API. Il metodo resolve può restituire un Observable, una promessa o solo un tipo personalizzato.

Nota: è importante menzionare che solo i dati risolti saranno restituiti tramite questo metodo.

Questo è un errore che molte persone fanno quando usano i resolver per la prima volta! Quindi è necessario completarli prima di inviarli al router.

Inviare dati al router tramite un resolver

Lasciate che vi mostri cosa intendo nella sezione precedente. Diciamo che avete un metodo che restituisce un osservabile:

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

Quindi, ora, vedrete che l’abbonamento che abbiamo, non colpisce mai. Questo è causato dal fatto che non state inviando i dati correttamente. Non stai completando la sottoscrizione. Per risolvere l’errore, devi completare la sottoscrizione aggiungendo un’altra linea di codice.

observer.complete();

Nel caso in cui stai restituendo una promessa, devi risolverla prima di inviare i dati al router.

Implementazione di un Route Resolver

Dopo aver finito con la spiegazione di base e perché e come usare un route resolver, iniziamo ad implementarne uno. In questo breve esempio, presumo che abbiate familiarità con i comandi CLI di Angular e come creare un nuovo progetto, quindi vi dimostrerò solo il codice obbligatorio. Potete trovare il progetto demo tramite il repository GitHub alla fine dell’articolo.

In questo esempio, userò jsonplaceholder come finta API per recuperare i dati dell’utente per dimostrare le chiamate API con i route resolver.

Prima di tutto, avremo bisogno di un servizio che recuperi i dati utente per noi. In questo servizio, abbiamo una funzione chiamata getUsers() che restituisce un osservabile.

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

È importante non sottoscrivere la funzione getUsers. Il route resolver chiamato UserResolver si prenderà cura di questo per voi. Il prossimo passo è quello di creare un nuovo servizio chiamato UserResolver che implementerà la funzione resolve dell’interfaccia Resolve del router.

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

Questo servizio, UserResolver, si iscriverà automaticamente all’osservabile getUsers e fornirà al router i dati recuperati. In caso di errore, durante il recupero dei dati, è possibile inviare un osservabile vuoto e il router non procederà al percorso.

La navigazione sarà terminata a questo punto. Quest’ultimo passo consiste nel creare un componente che sarà chiamato quando l’utente andrà alla rotta /users. In genere, senza un Resolver, sarà necessario recuperare i dati sull’hook ngOnInit del componente e gestire gli errori causati da “nessun dato” esistente. Il componente dell’utente è semplice. Prende solo i dati dell’utente dal ActivatedRoute e li visualizza in una lista non ordinata.

Dopo aver creato il componente dell’utente, hai bisogno di definire le rotte e dire al router di usare un resolver ( UserResolver). Questo può essere ottenuto con il seguente codice nella app-routing.modulte.ts.

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

È necessario impostare la proprietà resolve nella rotta dell’utente e dichiarare il UserResolver. I dati saranno passati in un oggetto con una proprietà chiamata utenti. Dopo questo, avete quasi finito. C’è solo una cosa da fare. Devi portare i dati recuperati nel componente degli utenti attraverso il ActivatedRoute con il seguente codice.

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

Poi, puoi semplicemente visualizzarli in HTML senza alcuna dichiarazione *ngIf ( *ngIf="users && users.length > 0 ) perché i dati saranno lì prima che il componente venga caricato.

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

Sommario

Concludendo questo articolo sui Route Resolver, vorrei sottolineare ancora una volta i vantaggi che ci forniscono. In primo luogo, ci evitano i fastidiosi controlli che devono essere fatti a livello di HTML, in modo da non avere problemi finché i nostri dati non vengono ricevuti. In secondo luogo, ci permettono di concentrarci di più sull’esperienza dell’utente, dato che possiamo interrompere la navigazione se si verifica un errore di dati durante il fetching, senza dover caricare il componente di anteprima. Possiamo anche fare di più prima di completare la navigazione, come allegare altra logica o mappare i dati prima di accedere al componente caricato.

Il codice demo è disponibile su GitHub.

Altre letture

  • Tutorial Angular: Angular 7 e il RESTEasy Framework.
  • Costruire un’applicazione Angular 5 passo dopo passo.
  • Applicazione Angular 7 + Spring Boot: Esempio di Hello World.

Lascia un commento