Les Resolvers avec Angular
Salut les dévs, aujourd'hui nous allons voir un concept important dans Angular qui peut s'avérer parfois être très utile: ce qu'on appelle les Resolvers. Un Resolver est service Angular qui permet de récupérer les données (via une API par exemple) dont nous avons besoin avant d'activer la route et de charger notre composant. Voyons un cas pratique: imaginer que nous avons une liste d'utilisateurs ou d'articles truc du genre, et que nous voulons accéder aux détails d'un article. Nous avons le code ci dessous qui récupère les détails pour un article:
// post-details.component.ts
import { Component, OnInit } from "@angular/core";
import { Post } from "../model/post";
import { PostService } from "../core/post.service";
import { ActivatedRoute } from "@angular/router";
@Component({
selector: 'app-post-details',
templateUrl: './post-details.component.html'
})
export class PostDetailsComponent implements OnInit {
post: Post;
constructor(private postService: PostService, private route: ActivatedRoute) { }
ngOnInit() {
console.log('postDetails run...');
const id = +this.route.snapshot.paramMap.get('id');
this.postService.getPost(id).subscribe(post => {
console.log(post);
this.post = post;
});
}
}
Cette classe permet de récupérer un article via notre service en lui passant en paramètre l'identifiant de cet article et l'assigne à notre objet Post. Ci-dessous le template:
<!-- post-details.component.html -->
<h2>{{post.title}}</h2>
<p>{{post.body}}</p>
Nous avons les détails de notre article qui s'affiche, c'est super mais un petit instant, regardons au niveau de notre console:
Donc la bien que le titre et le texte de notre article s'affiche, on a un bug au niveau de la console qui nous dit que nos propriétés sont undefined. Cela est dû au fait que notre objet post est à l'état undefined au moment où notre composant a été initialisé, vu que nous récupérons nos données de manière asynchrone.
Pour remédier à cela, une première alternative serait d'utiliser l'opération ? Safe Navigation Operator sur la propriété, ceci permet de prévenir si l'objet est null ou non défini. Cependant, imaginez qu'on utilise notre objet post dans plusieurs endroits de notre template, ça pourrait rapidement être fastidieux d'ajouter ? partout. Voyons une autre alternative plus clean, les Resolvers.
Les Resolvers sont définis au niveau de la configuration du routage. Comme dit précédemment, ils nous permettent de récupérer tous les données nécessaire au composant avant d'activer la router et afficher le template. Les Resolvers sont des services et doivent être renseignés au niveau de la section providers du module. Commençons par modifier notre routing et ajouter le paramètre resolver qui est optionnel.
// app.routing.ts
import { Routes, RouterModule } from '@angular/router';
import { PostsComponent } from './posts/posts.component';
import { PostDetailsComponent } from './posts/post-details.component';
import { PostResolve } from './core/post.resolve';
const appRoutes: Routes = [
{ path: '', component: PostsComponent },
{ path: 'posts', component: PostsComponent },
// c'est cette section qui nous interesse
{
path: 'posts/:id',
component: PostDetailsComponent,
resolve: {post: PostResolve} // notre service PostResolve
}
];
export const appRouting = RouterModule.forRoot(appRoutes);
Créons maintenant notre service:
// post.resolve.ts
import { Injectable } from "@angular/core";
import { Resolve, ActivatedRouteSnapshot } from "@angular/router";
import { Post } from "../model/post";
import { PostService } from "./post.service";
@Injectable()
export class PostResolve implements Resolve<Post> {
constructor(private postService: PostService) {}
resolve(route: ActivatedRouteSnapshot) {
// paramMap permet de récupérer les paramétres de la route associée
return this.postService.getPost(+route.paramMap.get('id'));
}
}
Et notre controller details post devient:
// post-details.component.ts
import { Component, OnInit } from "@angular/core";
import { Post } from "../model/post";
import { PostService } from "../core/post.service";
import { ActivatedRoute } from "@angular/router";
@Component({
selector: 'app-post-details',
templateUrl: './post-details.component.html'
})
export class PostDetailsComponent implements OnInit {
post: Post;
constructor(private route: ActivatedRoute) { }
ngOnInit() {
console.log('postDetails run...');
// permet d'accéder au données gérees par notre route utilisant le resolver
this.post = this.route.snapshot.data['post'];
}
}
Maintenant, au lieu d'injecter notre service au niveau de notre composant, nous l'utilisons dans notre service Resolver. C'est tout pour ce petit tutoriel sur l'utilisation des Resolvers.
Le code est disponible sur Github.