Appearance
Création d'un resolver
Pourquoi un resolver ?
Un resolver dans Angular est comme un assistant qui prépare les données nécessaires avant que vous n'arriviez sur une page. Imaginez que vous allez au restaurant : le resolver serait comme le serveur qui prépare votre table et apporte le menu avant même que vous ne vous asseyiez.
Dans le contexte d'Angular, voici ce qu'un resolver fait :
Préparation des données : Il récupère les informations nécessaires pour une route spécifique avant que la page ne soit affichée.
Chargement anticipé : Au lieu d'attendre que le composant soit chargé pour commencer à récupérer les données, le resolver les obtient à l'avance.
Amélioration de l'expérience utilisateur : Cela permet d'éviter d'afficher une page vide ou un indicateur de chargement, car les données sont déjà prêtes lorsque la page s'affiche.
Gestion des erreurs : Si quelque chose ne va pas lors de la récupération des données, le resolver peut rediriger l'utilisateur vers une page d'erreur avant même que la route ne soit activée.
Structure
Tout d'abord, voici un exemple simple d'une structure de service de résolution :
plaintext
app/
|- user-resolver.service.ts
|- user.service.ts
|- user.interface.ts
|- app.component.ts
|- app.router.ts
|- app.config.ts
|- user-list.component.ts
plaintext
app/
|- services/
| |- user.service.ts
| |- user.interface.ts
| |- resolvers/
| |- user-resolver.service.ts
|- app.component.ts
|- app.router.ts
|- app.config.ts
|- pages/
| |- user-list.component.ts
La fonction du resolver
ts
import { inject } from '@angular/core';
import { ResolveFn } from '@angular/router';
import { Observable } from 'rxjs';
import { User } from './user.interface';
export const userResolver: ResolveFn<User> = (
route,
state
): Observable<User> => {
const userService = inject(UserService);
return userService.get(route.params['userId']);
};
ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { User } from './user.interface';
@Injectable({
providedIn: 'root'
})
export class UserService {
http = inject(HttpClient);
get(userId: number): Observable<User> {
return this.http.get<User>(`/api/users/${productId}`);
}
}
ts
export interface User {
id: number;
name: string;
}
Ajouter le resolver à la route
Pour utiliser ce service de résolution dans votre routeur, vous devez l'inclure dans la configuration de la route en utilisant la propriété resolve
. Allons dans app.router.ts
:
ts
import { Routes } from '@angular/router';
import { UserListComponent } from './user-list.component';
import { userResolver } from './user-resolver.service';
const routes: Routes = [
{
path: 'user/:userId',
component: UserListComponent,
resolve: {
user: userResolver
}
}
];
(astuce 1) Accéder aux données résolues dans le composant
Enfin, vous pouvez accéder aux données résolues dans le composant de la route en utilisant la propriété data
de l'objet ActivatedRoute
:
ts
import { Component, OnInit, inject } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { User } from './user.interface';
@Component({
selector: 'app-user-list',
standalone: true,
templateUrl: `
<h1>User</h1>
<p>{{ user.name }}</p>
`
})
export class UserListComponent {
private route = inject(ActivatedRoute);
user: User = this.route.snapshot.data.user
}
Note
Le resolver est appelé avant que le composant ne soit chargé, donc les données sont déjà prêtes lorsque le composant est initialisé.
Avec un observable
Si vous souhaitez utiliser un observable, vous pouvez retourner un Observable
dans le resolver.
ts
import { Component, OnInit, inject } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { User } from './user.interface';
@Component({
selector: 'app-user-list',
standalone: true,
imports: [AsyncPipe],
templateUrl: `
<h1>User</h1>
<p>{{ (user$ | async).name }}</p>
`
})
export class UserListComponent {
private route = inject(ActivatedRoute);
user$ = this.route.data.pipe(map(data => data['user']))
}
(astuce 2) Récupérer les données avec @Input()
Une autre approche élégante pour accéder aux données résolues dans votre composant est d'utiliser la propriété @Input()
. Cette méthode simplifie encore davantage l'accès aux données et rend votre code plus lisible.
Voici comment vous pouvez implémenter cette approche :
ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter, withComponentInputBinding } from '@angular/router';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes, withComponentInputBinding()),
provideHttpClient()
]
};
Ensuite, dans votre composant, vous pouvez utiliser @Input()
pour accéder directement aux données résolues :
ts
import { Component, Input } from '@angular/core';
import { User } from './user.interface';
@Component({
selector: 'app-user-list',
standalone: true,
template: `
<h1>User</h1>
<p>{{ user.name }}</p>
`
})
export class UserListComponent {
@Input() user: User = {} as User
}
AVANTAGE
Cette approche avec @Input()
présente plusieurs avantages :
- Code plus concis et plus lisible
- Pas besoin d'injecter et gérer
ActivatedRoute
- Typage plus strict des données
- Meilleure réutilisabilité du composant
ATTENTION
N'oubliez pas d'activer withComponentInputBinding()
dans votre configuration, sinon les @Input()
ne fonctionneront pas avec les données résolues.
Gestion des erreurs
Il est important de gérer les erreurs qui peuvent survenir lors de la résolution des données. Voici comment vous pouvez le faire :
ts
import { inject } from '@angular/core';
import { ResolveFn, Router } from '@angular/router';
import { Observable, catchError } from 'rxjs';
import { User } from './user.interface';
export const userResolver: ResolveFn<User> = (
route,
state
): Observable<User> => {
const userService = inject(UserService);
const router = inject(Router);
return userService.get(route.params['userId']).pipe(
catchError((error) => {
router.navigate(['/error']);
throw error;
})
);
};
CONSEIL
Il est recommandé de toujours gérer les erreurs dans vos resolvers pour éviter que votre application ne reste bloquée si la résolution échoue.