Appearance
Récupérer les informations de route avec withComponentInputBinding()
Compatibilité
Cette fonctionnalité est disponible depuis Angular 16.
La transmission d'informations entre les composants est un besoin courant dans une application Angular. Nous allons voir comment utiliser withComponentInputBinding()
pour passer et récupérer des données de manière élégante.
Pourquoi utiliser withComponentInputBinding()
?
Imaginez que vous développez une application de gestion de bibliothèque. Avant, pour afficher les détails d'un livre à partir de son ID dans l'URL, vous deviez injecter le service ActivatedRoute
, vous abonner aux changements, et extraire manuellement les paramètres. C'était verbeux et répétitif. Avec withComponentInputBinding()
, c'est comme si vous aviez un assistant qui transmet automatiquement ces informations à votre composant !
Pourquoi utiliser withComponentInputBinding() ?
- Injection simplifiée des données du routeur : Au lieu d'injecter
ActivatedRoute
et d'extraire manuellement les données, vous pouvez simplement déclarer des entrées de composants portant les mêmes noms que les clés de données du routeur, etwithComponentInputBinding()
les remplira automatiquement. - Découplage : Les composants n'ont plus besoin de connaître le routeur ou
ActivatedRoute
pour accéder aux données de la route. Cela permet de découpler la logique du composant de l'implémentation du routeur. - Amélioration de l'organisation du code : L'utilisation d'entrées de composants permet de concentrer la logique des composants sur leurs responsabilités principales, tandis que l'injection des données du routeur est gérée séparément.
- Tests plus faciles : Avec
withComponentInputBinding()
, vous pouvez tester vos composants plus facilement, car les données du routeur sont injectées automatiquement et vous n'avez pas besoin de simuler ou de bloquerActivatedRoute
.
1. Définition des routes
Créons nos routes pour gérer les utilisateurs :
ts
import { Routes } from '@angular/router';
export const routes: Routes = [
{
path: 'users/:id',
component: UserComponent
}
];
2. Configuration du Router
D'abord, nous devons activer la liaison automatique des paramètres de route aux inputs des composants :
ts
import { provideRouter, withComponentInputBinding } from '@angular/router';
import { ApplicationConfig } from '@angular/core';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes, withComponentInputBinding())
]
};
HÉRITAGE DES PARAMÈTRES
Imaginons que vous ayez une structure de routes imbriquées comme ceci :
- /users/:userId (route parent)
- /users/:userId/posts/:postId (route enfant)
Par défaut, le composant de la route enfant n'a accès qu'à son propre paramètre postId
. Avec paramsInheritanceStrategy: 'always'
, le composant enfant pourra aussi accéder au userId
de la route parente.
ts
import { provideRouter, withComponentInputBinding, withRouterConfig } from '@angular/router';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes,
withComponentInputBinding(),
withRouterConfig({
paramsInheritanceStrategy: 'always'
})
)
]
};
3. Création du composant
Voici comment récupérer l'ID de l'utilisateur dans notre composant :
ts
import { Component, Input } from '@angular/core';
import { AsyncPipe } from '@angular/common';
@Component({
standalone: true,
selector: 'app-user',
imports: [AsyncPipe],
template: `
@if (user$ | async; as user) {
<div>
<h2>{{ user.name }}</h2>
<p>{{ user.email }}</p>
</div>
}
`
})
export class UserComponent {
private userService = inject(UserService);
user$!: Observable<User>;
@Input()
set id(userId: string) {
this.user$ = this.userService.getUser(userId);
}
}
ASTUCE
L'utilisation de set
avec l'Input
permet de réagir automatiquement aux changements de paramètres de route, même si l'utilisateur navigue entre différents profils sans changer de composant.
4. Navigation vers l'utilisateur
Pour naviguer vers un utilisateur spécifique :
ts
import { Component } from '@angular/core';
@Component({
standalone: true,
selector: 'app-home',
template: `
@for (user of users; track user.id) {
<div>
<span>{{ user.name }}</span>
<button (click)="navigateToUser(user.id)">Voir le profil</button>
</div>
}
`
})
export class HomeComponent {
private router = inject(Router);
navigateToUser(userId: number) {
this.router.navigate(['/users', userId]);
}
}
ATTENTION
Assurez-vous que les types de paramètres correspondent entre votre route et votre Input. Si votre route attend un nombre mais que vous recevez une chaîne, vous devrez faire la conversion appropriée.
Les données statiques avec data
Imaginons que vous ayez un site e-commerce. Chaque catégorie de produits a un titre et une description spécifique. Au lieu de coder en dur ces informations dans les composants, vous pouvez les définir dans les routes.
ts
import { Component, Input } from '@angular/core';
@Component({
standalone: true,
selector: 'app-user-list',
template: `
<h1>{{ title }}</h1>
<p>{{ description }}</p>
@if (role === 'admin') {
<button>Ajouter un utilisateur</button>
}
`
})
export class UserListComponent {
@Input() title = '';
@Input() description = '';
@Input() role = '';
}
ts
import { Routes } from '@angular/router';
export const routes: Routes = [
{
path: 'users',
component: UserListComponent,
data: {
title: 'Liste des utilisateurs',
description: 'Gérez tous vos utilisateurs',
role: 'admin'
}
}
];
Les Resolvers pour le chargement de données
Plus d'infos sur les resolvers ici.
ts
import { Component, Input } from '@angular/core';
@Component({
standalone: true,
selector: 'app-user',
template: `
<div>
<h2>{{ user.name }}</h2>
<p>{{ user.email }}</p>
</div>
`
})
export class UserComponent {
@Input() user!: User;
}
ts
import { Routes } from '@angular/router';
import { userResolver } from './user.resolver';
export const routes: Routes = [
{
path: 'users/:id',
component: UserComponent,
resolve: {
user: userResolver
}
}
];
ts
import { ResolveFn } from '@angular/router';
import { inject } from '@angular/core';
import { UserService } from './user.service'; // Le service qui utiliser pour récupérer les données
export const userResolver: ResolveFn<User> = (route) => {
const userService = inject(UserService);
return userService.getUser(route.paramMap.get('id')!);
};