Appearance
Utiliser input() pour communiquer avec un composant enfant
La communication entre composants est un concept fondamental en Angular. Dans ce tutoriel, nous allons voir comment utiliser la nouvelle syntaxe input()
pour permettre à un composant parent de transmettre des données à son composant enfant.
Utiliser les composants avec des signaux
Depuis Angular 17.x, les signaux sont introduits en tant que réactifs comme alternative aux décorateurs @Input()
, @Output()
, etc.
Donc, pour utiliser input()
, output()
, model()
, contentChild()
, contentChildren()
, viewChild()
et viewChildren()
, il est essentiel de comprendre la notion des signaux.
Lisez l'article sur les signaux pour en savoir plus.
Comprendre le concept avec un exemple concret
Imaginons une application de gestion d'utilisateurs où nous avons une liste d'utilisateurs dans un composant parent, et nous voulons afficher les détails d'un utilisateur dans un composant enfant. C'est comme un tableau d'affichage où le parent possède toutes les informations et décide quelles informations il veut partager avec ses enfants.
ts
import { Component, input } from '@angular/core';
import { User } from './user';
@Component({
selector: 'app-user-details',
standalone: true,
template: `
<div class="user-card">
<h2>{{ user().name }}</h2>
<p>Email: {{ user().email }}</p>
<p>Username: {{ user().username }}</p>
</div>
`
})
export class UserDetailsComponent {
user = input<User>({} as User);
}
ts
import { Component } from '@angular/core';
import { UserDetailsComponent } from './user-details.component';
import { User } from './user';
@Component({
selector: 'app-user-list',
standalone: true,
imports: [UserDetailsComponent],
template: `
<div class="user-list">
@for (userData of users; track userData.id) {
<app-user-details [user]="userData" />
}
</div>
`
})
export class UserListComponent {
users: User[] = [
{ id: 1, name: 'John Doe', username: 'johndoe', email: '[email protected]' },
{ id: 2, name: 'Jane Smith', username: 'janesmith', email: '[email protected]' }
];
}
ts
export interface User {
id: number;
name: string;
username?: string;
email: string;
address?: {
street: string;
suite: string;
city: string;
zipcode: string;
geo: {
lat: string;
lng: string;
}
};
phone?: string;
website?: string;
company?: {
name: string;
catchPhrase: string;
bs: string;
};
}
Attention
Lorsque vous utilisez input()
, n'oubliez pas que :
- La valeur est accessible via une fonction (exemple:
user()
) - Les inputs sont en lecture seule
- Les inputs doivent être déclarés au niveau de la classe
Les différentes façons d'utiliser input()
1. Input avec valeur par défaut
ts
export class UserDetailsComponent {
// L'input aura une valeur par défaut si aucune valeur n'est fournie
username = input('Anonymous');
}
2. Input requis
ts
export class UserDetailsComponent {
// L'input doit obligatoirement être fourni par le parent
user = input.required<User>();
}
3. Input avec transformation
ts
export class UserDetailsComponent {
// L'input sera transformé avant d'être utilisé
name = input('', {
transform: (value: string) => {
return value.toUpperCase();
}
});
}
Transformations intégrées
Angular inclut deux fonctions de transformation intégrées pour les scénarios les plus courants : la conversion des valeurs en booléens et en nombres.
typescript
import { Component, input, booleanAttribute, numberAttribute } from '@angular/core';
@Component({
selector: 'app-custom-slider',
standalone: true,
template: `<div>Slider Component</div>`
})
export class CustomSlider {
disabled = input(false, { transform: booleanAttribute });
value = input(0, { transform: numberAttribute });
}
booleanAttribute : Imitant le comportement des attributs booléens standard HTML, la présence de l'attribut indique une valeur "true". Cependant,
booleanAttribute
d'Angular traite la chaîne littérale "false" comme le booléen false.numberAttribute : Tente de convertir la valeur donnée en un nombre, produisant
NaN
si la conversion échoue.
Alias d'input
Vous pouvez spécifier l'option alias
pour changer le nom d'un input dans les templates.
typescript
import { Component, input } from '@angular/core';
@Component({
selector: 'app-custom-slider',
standalone: true,
template: `<div>Slider Component</div>`
})
export class CustomSlider {
value = input(0, { alias: 'sliderValue' });
}
Dans le template, vous pouvez utiliser l'alias comme suit :
html
<app-custom-slider [sliderValue]="50"></app-custom-slider>
Remarque
- Cet alias n'affecte pas l'utilisation de la propriété dans le code TypeScript.
- Bien que vous deviez généralement éviter d'aliaser les inputs pour les composants, cette fonctionnalité peut être utile pour renommer des propriétés tout en préservant un alias pour le nom d'origine ou pour éviter les collisions avec le nom des propriétés des éléments DOM natifs.
Qu'est-ce que @Input()
?
Avant Angular 17
Le décorateur @Input()
est la façon traditionnelle de définir des inputs dans Angular avant la version 17.
Le décorateur @Input()
permet à un composant enfant d'accepter une valeur passée depuis son composant parent.
Imaginez que nous ayons un composant Parent
qui contient un composant Enfant
. Si le composant Parent
veut passer une valeur au composant Enfant
, il peut le faire en utilisant la syntaxe de binding de propriété d'Angular. Voici uin exemple simple :
Composant Enfant (enfant.component.ts
) :
typescript
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-enfant',
template: `<h3>Message de mon parent: {{ message }}</h3>`,
standalone: true
})
export class EnfantComponent {
@Input() message = '';
}
Ici, le composant Enfant
attend un message
de son parent.
Composant Parent (parent.component.ts
) :
typescript
import { Component } from '@angular/core';
import { EnfantComponent } from './enfant.component';
@Component({
selector: 'app-parent',
imports: [EnfantComponent],
standalone: true,
template: `
<div>
<h2>Je suis le composant Parent</h2>
<app-enfant [message]="monMessage"></app-enfant>
</div>
`
})
export class ParentComponent {
monMessage = "Salut Enfant!";
}
Dans le composant Parent
, nous avons défini un message, monMessage
, et nous le passons au composant Enfant
à l'aide de la syntaxe de binding de propriété : [message]="monMessage"
.
En fait, en mettant @Input() sur la propriété, on vient tout simplement de créer un attribut HTML qui s'appelle
message
et qui est lié à la propriétémessage
du composant 😃
Bonnes pratiques 👍
- Utilisez les décorateurs de classe
@Input()
et@Output()
au lieu des propriétés inputs et outputs des métadonnées@Directive
et@Component
(voir Angular Style Guide). - Évitez les alias d'entrée et de sortie, sauf s'ils servent un objectif important (Exemple:
@Input('aliasMessage') message: string = '';
). Les alias peuvent rendre le code plus difficile à lire et à comprendre. (Voir Angular Style Guide). - Initialisez les propriétés d'entrée avec une valeur par défaut (Exemple:
@Input() message: string = '';
). Cela permet d'éviter les erreurs si la valeur n'est pas définie dans le composant parent. (Voir Angular Style Guide)
Angular 16+
Si vous utilisez Angular 16+ et qu'il propose une fonctionnalité pour définir des entrées requises à l'aide de la décoration @Input
, voici comment cela pourrait fonctionner d'après votre code :
typescript
import { Component, Input } from '@angular/core';
@Component({...})
export class App {
@Input({ required: true }) title: string = '';
}
Dans cet exemple, vous utilisez la décoration @Input()
avec une option required: true
pour déclarer que la propriété title
est requise.