Faire un breadcrumb dans un projet Angular
Table des matières
- Faire un service pour récupérer la configuration
- Faire un composant pour afficher le breadcrumb
- Faire un resolver pour afficher des données dans le breadcrumb
- Utilisation
Faire un service pour récupérer la configuration
Notre breadcrumb va se servir de la configuration des routes pour trouver le contenu qu'il doit afficher, pour cela, nous allons créer un service qui va écouter les modifications de la route et reconstruire une liste avec une fonction recursive.
import { Injectable } from '@angular/core';
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { filter, distinctUntilChanged, map } from 'rxjs/operators';
export interface BreadcrumbItem {
label: string;
path?: string;
}
@Injectable({ providedIn: 'root' })
export class BreadcrumbService {
public items$: Observable<BreadcrumbItem[]>;
constructor(
private _router: Router,
private _route: ActivatedRoute
) {
this.items$ = this._router.events.pipe(
filter(event => event instanceof NavigationEnd),
distinctUntilChanged(),
map(_ => this._buildBreadCrumb(this._route.root))
);
}
private _buildBreadCrumb(route: ActivatedRoute, url: string = '', breadcrumbs: BreadcrumbItem[] = []): BreadcrumbItem[] {
const newBreadcrumbs = [...breadcrumbs];
const path = route.snapshot.url.map(segment => segment.path).join('/');
const nextUrl = `${url}/${path}`.replace('//', '/');
if (route.routeConfig && route.routeConfig.data && route.routeConfig.data.breadcrumb) {
let data = '';
if (route.routeConfig.data.breadcrumb[0] === '@') {
route.routeConfig.data.breadcrumb.split('.').forEach((level: string, index: number) => {
if (index === 0) {
data = route.snapshot.data[level.substr(1)];
} else {
data = !!data ? (data as any)[level] : null;
}
});
} else {
data = route.routeConfig.data.breadcrumb;
}
newBreadcrumbs.push({
label: data,
path: nextUrl
});
}
if (route.firstChild) {
return this._buildBreadCrumb(route.firstChild, nextUrl, newBreadcrumbs);
}
return newBreadcrumbs;
}
}
La liste d'éléments à afficher est stockée sous forme d'Observable
et pourra être lue de façon dynamique.
Faire un composant pour afficher le breadcrumb
Pour lire cette liste d'éléments, nous allons créer un composant qui bouclera sur notre Observable
et qui affichera son contenu.
import { Component } from '@angular/core';
import { BreadcrumbService } from './breadcrumb.service';
@Component({
selector: 'nav[app-breadcrumb]',
template: `
<ng-container *ngFor="let item of breadcrumbService.items$ | async; let last = last">
<a *ngIf="!last" [routerLink]="item.path">{{ item.label }}</a>
<span *ngIf="last">{{ item.label }}</span>
<span *ngIf="!last">/</span>
</ng-container>`,
})
export class BreadcrumbComponent {
constructor(
public breadcrumbService: BreadcrumbService
) {}
}
Faire un resolver pour afficher des données dans le breadcrumb
Enfin, nous devons aussi créer un resolver pour nos objets qui devront être chargés avant l'arrivée du composant.
C'est une étape obligatoire pour tous les objets qui seront chargés en fonction de l'url, mais elle n'est pas necessaire pour les textes écrits en dur dans le breadcrumb.
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot } from '@angular/router';
import { of } from 'rxjs';
import { User, UserService } from './user.service';
@Injectable({ providedIn: 'root' })
export class UserResolver implements Resolve<Promise<User | null>> {
constructor(
private userService: UserService,
) {}
resolve(route: ActivatedRouteSnapshot) {
const id = route.paramMap.get('id');
return of(this.userService.getUser(!!id ? +id : 0)).toPromise();
}
}
Utilisation
Pour afficher le breadcrumb, nous pouvons simplement appeler son sélecteur comme ceci:
<nav app-breadcrumb></nav>
Et pour ce qui est du contenu du breadcrumb, nous allons le définir dans la configuration des routes:
const routes: Routes = [
{
path: '',
data: { breadcrumb: `User` },
children: [
{ path: '', component: UserListComponent },
{ path: 'create', component: UserCreateComponent, data: { breadcrumb: 'Create' } },
{ path: ':id', component: UserShowComponent, resolve: { user: UserResolver }, data: { breadcrumb: '@user.name' } },
]
},
];