Salta al contenuto principale

Usare i Resolvers di Angular per risolvere i dati prima che la rotta venga attivata

Profile picture for user luca77king

Angular è uno dei framework più popolari e potenti per lo sviluppo di applicazioni web moderne. Una delle sue principali forze è l'abilità di gestire in modo efficiente i dati asincroni, una sfida comune nello sviluppo di applicazioni web dinamiche. In questo contesto, i "resolvers" giocano un ruolo cruciale.

I resolvers in Angular sono strumenti potenti che aiutano gli sviluppatori a gestire il caricamento dei dati prima che una vista sia visualizzata. In pratica, assicurano che tutti i dati necessari siano disponibili prima che l'utente interagisca con una pagina dell'applicazione. Questo non solo migliora l'esperienza utente, evitando di visualizzare una pagina incompleta o di attendere il caricamento dei dati, ma anche ottimizza la gestione delle risorse e la reattività dell'applicazione.

Cosa sono i Resolvers in Angular?

Fino a qualche releases fa, un resolver era una classe che implementa l'interfaccia Resolve, ma nelle versioni recenti di Angular, il concetto di resolver è stato evoluto con l'introduzione di ResolveFn, una funzione che sostituisce l'approccio basato su classi per implementare resolvers. il principale vantaggio di ResolveFn è una maggiore flessibilità e semplicità nel definire logiche di recupero dati. Invece di creare una classe completa, ResolveFn permette agli sviluppatori di Angular di specificare direttamente una funzione che determina come e quali dati vengono recuperati prima di navigare verso una rotta specifica. Questo approccio è più diretto e può ridurre la quantità di boilerplate necessaria per gestire i resolvers.

Lo scopo principale rimane lo stesso: recuperare dati asincroni prima del rendering di una rotta specifica. In termini semplici, un resolver agisce come un intermediario tra il routing di Angular e la visualizzazione finale, assicurando che tutti i dati necessari siano stati caricati prima che l'utente possa vedere o interagire con la pagina.

I resolvers sono particolarmente utili in scenari come il pre-caricamento di dati da un'API remota, la verifica della disponibilità di certi dati prima di consentire l'accesso a una rotta o la preparazione di dati complessi che richiedono elaborazioni multiple.

Un classico esempio di utilizzo di un resolver potrebbe essere una pagina di dettaglio di un blog. Invece di caricare la pagina e poi i dati del post del blog, un resolver può essere utilizzato per prima recuperare il post del blog specifico. Solo dopo che i dati sono stati completamente caricati e sono pronti, la pagina viene renderizzata con i dati già disponibili.

Implementare un Resolver di Base

Consideriamo un servizio BlogService che ha un metodo getPost(slug: string) il quale restituisce un Observable di un post del blog. Il nostro obiettivo è utilizzare un resolver per recuperare un post del blog basato sullo slug prima di navigare alla pagina del post.

Cominciamo creado il resolver usando la cli di Angular

ng g r post --path="src/app/resolvers" --skip-tests

Il nostro resolver si presenterà così

import { ResolveFn } from '@angular/router';

export const postResolver: ResolveFn<boolean> = (route, state) => {
  return true;
};

Mentre route fornisce accesso ai parametri e ai dati relativi alla rotta specifica attualmente in risoluzione, state permette di accedere a informazioni più ampie sullo stato di routing complessivo. Ad esempio, potresti voler sapere da quale rotta proviene l'utente o verso quale rotta si sta dirigendo. Adesso iniettiamo il servizio nei parametri della funzione

import { ResolveFn } from '@angular/router';
import {BlogService} from "../services/blog.service";
import {inject} from "@angular/core";

export const postResolver: ResolveFn<boolean> = (
  route, 
  state,
  blogService: BlogService = inject(BlogService),
  ) => {
  return true;
};

Ora facciamo in modo di recupeare lo slug dal router ed utilizzare il metodo getPost del servizio

import { ResolveFn } from '@angular/router';
import {BlogService} from "../services/blog.service";
import {inject} from "@angular/core";
import {Blog} from "../interfaces/blog/blog";
import {of} from "rxjs";

export const postResolver: ResolveFn<Blog|null> = (
  route,
  state,
  blogService: BlogService = inject(BlogService),
  ) => {
  const postSlug = route.paramMap.get('slug');
  if(postSlug) {
    return blogService.getPost(postSlug); 
  }
  return of(null);
};

Nel componente che utilizza questi dati, sarà necessario verificare se i dati sono presenti o meno. Se il resolver ritorna null, il componente può decidere come reagire: mostrare un messaggio di errore, reindirizzare l'utente a un'altra pagina, o visualizzare contenuto alternativo.

Dopo aver creato il resolver, il passo successivo è configurarlo nel modulo di routing. Questo si fa associando il resolver a una rotta specifica nel RouterModule.

const routes: Routes = [
  {
    path: 'list',
    component: BlogListComponent,
    resolve: {
      routeResolver: blogListResolver
    }
  },
  {
    path: 'detail/:slug',
    component: BlogDetailComponent,
    resolve: {
      routeResolver: postResolver
    }
  },
];

In questo esempio, quando si naviga verso detail/:slug, Angular eseguirà PostResolver prima di caricare BlogDetailComponent. I dati recuperati saranno disponibili nel BlogDetailComponent attraverso ActivatedRoute.

ngOnInit(): void {
      this.activatedRoute.data.subscribe(data => {
            if (data.hasOwnProperty('routeResolver') && data['routeResolver']) {
                  this.blogData = data['routeResolver']['blog'];
            }
      });
}

Resolvers Avanzati e Strategie di Ottimizzazione

Nel mondo reale, le applicazioni Angular spesso richiedono il recupero di dati più complessi o multipli. In questi casi, i resolvers possono essere strutturati per gestire scenari più avanzati.

Gestire Dati Multipli: Un resolver può essere configurato per recuperare più pezzi di dati prima di risolvere una rotta. Ad esempio, se una pagina richiede sia i dettagli dell'utente sia i suoi post, puoi creare un resolver che recupera entrambi questi set di dati. Questo si può ottenere combinando più Observable con operatori RxJS come forkJoin o combineLatest.

Strategie di Caching: Per migliorare le prestazioni, i resolvers possono implementare una strategia di caching. Se i dati recuperati non cambiano frequentemente, puoi memorizzarli in cache e recuperarli dalla cache se disponibili, invece di effettuare una nuova richiesta al server. Questo riduce il carico sul server e migliora i tempi di caricamento per l'utente.

Gestione degli Errori: In un resolver, è essenziale gestire gli errori in modo che l'applicazione possa rispondere in modo appropriato. Ad esempio, se il recupero dei dati fallisce, potresti voler reindirizzare l'utente a una pagina di errore o visualizzare un messaggio.

Caso Studio: SSR e Resolvers

Uno degli aspetti più potenti dei resolvers in Angular è il loro uso nel contesto del Server-Side Rendering (SSR). Angular Universal, che consente SSR in Angular, può utilizzare resolvers per pre-caricare i dati necessari prima di inviare la pagina renderizzata al client.

a. Benefici SEO: I resolvers sono particolarmente utili per la SEO in Angular Universal. Poiché i dati della pagina vengono caricati lato server, i crawler dei motori di ricerca ricevono una pagina completamente renderizzata, migliorando la visibilità e l'indicizzazione dei contenuti.

b. Miglioramento dell'Esperienza Utente: Utilizzando i resolvers con SSR, puoi offrire agli utenti un caricamento più rapido delle pagine e una migliore esperienza utente, soprattutto in situazioni di connessione lenta o dispositivi con prestazioni limitate.

Conclusione

I resolvers in Angular rappresentano uno strumento fondamentale nell'arsenale di ogni sviluppatore Angular. Offrono un metodo elegante e potente per gestire il caricamento asincrono dei dati, migliorare l'esperienza utente e ottimizzare le applicazioni per i motori di ricerca. Con una comprensione solida di come implementarli e utilizzarli in modi avanzati, puoi notevolmente elevare la qualità e le prestazioni delle tue applicazioni Angular.