image auteur
Huber GERARD 8min • 02-11-2020

Angular - Les composants

Angular - Les composants

Bienvenue sur CodeWise ! Si vous êtes nouveau ici, vous voudrez sans doute découvrir notre Starter Kit Angular : il contient une roadmap du parcours d'un dev Angular, ainsi qu'un cookbook des commandes les plus utiles et un extrait de formation. Cliquez ici pour le télécharger (c’est gratuit !)

Un composant Angular, à quoi ça sert ? Et comment les utiliser correctement ?

Un composant c'est quoi ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Component({
    selector: 'app-todo',
    templateUrl: './todo.component.html',  // or template: '<p>todo works!</p>',
    styleUrls: ['./todo.component.css'], // or styles: ['p { color: red }'],
    animations: [],
    encapsulation: ViewEncapsulation.Emulated,
    exportAs: 'todo'
})
export class TodoComponent  {
    @Input()
    options: any;
    @Output()
    notify: EventEmitter<string> = new EventEmitter<string>();
    todos: string[];
    constructor() { }
    addTodo(message: string) {
        this.todos.push(message);
    }
}
Exemple de composant Angular

En terme de concept

Un composant (component) est une unité de découpage visuel.

Les pages sont des composants, elles-mêmes découpées en plusieurs composants. L'idée est de séparer les pages web en composants pour ne pas se contenter de découper par langage (les fameux dossiers js, html, css ou autre).

On donne ainsi une structure logique au projet et à nos fichiers.

En terme de code

Un composant, c'est une classe Typescript avec des attributs, méthodes et constructeur et qui sert d'unité de découpage visuel dans un projet front. Il possède aussi des métadonnées qu'on lui injecte via la directive @Component.

On définit un composant dans Angular par un ensemble de ressources :

  • une portion de HTML
  • une ou des feuilles de styles associée(s) à ce HTML
  • une classe TypeScript ou JavaScript

Pour l'instancier, on utilise son sélecteur qui correspond à un tag HTML. Vous pouvez donc intégrer votre composant directement dans un bloc HTML, en utilisant son tag HTML associé.

Un composant est réutilisable autant de fois que nécessaire. Par conséquent, on ne code un composant qu'une fois pour plusieurs affichages.

On a également accès à son cycle de vie, via des hooks et il peut posséder n entrée/sortie pour communiquer avec d'autres composants.

Communication entre composants

Les méthodes dites view et content sont 2 approches différentes pour faire communiquer des composants.

La principale différence se trouve dans le partage du scope (la portée des variables) entre les composants parent et enfant.

La méthode view

  • Scope isolé entre les composants
  • Couplage composant parent/enfant lourd

Chaque composant gardant son scope isolé de l'autre, les directives @Inputs et @Outputs leur permettent de s’échanger des valeurs.

@Input

Input, permet de spécifier une valeur en entrée du composant. Pour l'utiliser, vous allez devoir modifier le code à 2 endroits :

  • Déclarez-le avec l'annotation @Input() dans le composant (fichier ts)
  • Liez-le en “one-way” binding avec [myInputVarNameHere] dans le template html du parent
1
2
3
4
5
6
7
8
@Component({
    selector: 'app-todo',
})
export class TodoComponent {
    @Input()
    todo: any;
    ...
}
todo.component.ts (enfant)
1
<app-todo [todo]="todo"></app-todo>
todo-list.component.html (parent)

@Output

@Output, permet de spécifier un événement en sortie du composant. Pour l'utiliser, vous devez modifier le code à 3 endroits :

  • Instanciez un attribut de type EventEmitter dans le composant (ts) qu'on décore avec la directive @Output
  • Liez-le en event-binding avec (myOutputVariableName) dans le template HTML du parent
  • Faîtes appel à la fonction emit() de l'EventEmitter au moment ou l'output doit se produire
1
2
3
4
5
6
7
8
@Component({
    selector: 'app-todo'
})
export class TodoComponent {
    @Output()
    notify: EventEmitter<any> = new EventEmitter();
    ...
}
todo.component.ts (enfant)
1
<app-todo (notify)="displayChecked($event)"></app-todo>
todo-list.component.html (parent)

La méthode content

  • pas d’input/output
  • scope partagé entre les composants
  • plus souple, plus facilement réutilisable

La balise ng-content

Pour mettre en place la projection de contenu de la méthode content, vous allez devoir modifier le code à 2 endroits :

  • Ajoutez du HTML à l'intérieur des balises de l'enfant, dans le fichier HTML du parent
  • Récupérez-le dans le composant enfant, en projetant son contenu dans une balise <ng-content>
1
2
3
4
5
6
7
8
9
@Component({
    selector: 'fa-input',
    template: `
            <i class="fa" [ngClass]="classes"></i>
            <ng-content></ng-content>
`
})
export class FaInputComponent {
}
fa-input.component.ts (composant enfant)
1
2
3
4
<h1>FA Input</h1>
<fa-input>
    <input type=”email” placeholder=”Email”/>
</fa-input>
app.component.html (parent)

Vous remarquez que, dans le composant parent, on a placé du contenu html à l'intérieur de notre balise de composant <fa-input>

Ce contenu est rendu disponible dans le composant fils via la balise <ng-content>. On dit que le contenu de la balise <fa-input> (parent) est projeté dans la balise <ng-content>

Vous connaissez maintenant les principes de la méthode view et de la projection de contenu !

Dans quel cas l'utiliser ?

La plupart du temps, on utilise la méthode view avec des Input/Output pour structurer la circulation des données au sein d'une page. Cela s'explique car le couplage entre les composants est fort, il s'agit d'un découpage spécifique à la page. De plus on souhaite isoler les scopes au possible.

Mais dans certains cas, il n'est vraiment pas pratique de garder des scopes isolés, et donc d'utiliser la méthode view.

Par exemple, si vous comptez réutiliser un composant souvent et que vous devez accéder à 5 de ses attributs, vous allez devoir gérer beaucoup d'Input/Output. C'est souvent le cas quand vous voulez séparer des formulaires sur plusieurs composants, ou rendre un formulaire générique.

Pour ces cas-là, vous pouvez choisir la méthode content.

Vous aurez donc un couplage plus faible mais une séparation néante des scopes entre les composants (le scope de l'enfant n'existe pas, celui du parent contient tout).

Si vous souhaitez bien comprendre les différents cas d'utilisation, je vous recommande ce tutoriel d'Angular University : Tutoriel pour maitriser la différence view/content

Passons maintenant au cycle de vie des composants !

Cycle de vie d'un composant

Cycle de vie d'un composant Angular

Angular vous permet d'exécuter du Javascript lors de certains évènements liés au cycle de vie du component.

Cela peut s'avérer utile, par exemple pour lancer un appel API au chargement d'une page ou bien pour supprimer une ressource lors de la destruction de l'instance du composant.

Référez vous à notre article sur Les cycles de vie d'un composant

ngOnChange

  • ne s’exécute pas si pas d’@Input
  • 1er hook appelé après l’appel au constructeur
  • déclenché à chaque changement de donnée en entrée @Input()

ngOnInit

  • intervient après l’appel de ngOnChange() 
  • déclenché après l’initialisation du composant
  • appelé uniquement une fois

ngDoCheck

  • intervient après l’appel de ngOnInit() 
  • ne s’exécute pas si ngOnChange est implémentée

ngAfterContentInit

  • s'exécute lorsqu’un content (ng-content) est initialisé
  • @ContentChildren et @ContentChild sont valorisées

ngAfterContentChecked

  • exécuté à chaque détection de changement
  • @ContentChildren et @ContentChild sont valorisées

ngAfterViewInit

  • exécuté une fois les vues enfants initialisé

ngAfterViewChecked

  • exécuté à chaque détection de changement
  • @ViewChildren et @ViewChild sont valorisées

ngOnDestroy

La prochaine étape, quand vous êtes à l'aise avec le concept de composants : Les services Angular.

Sources

Téléchargez votre Starter Kit Angular

Starter Kit Angular

En souscrivant vous recevrez :

  • Une roadmap d'un développeur Angular
  • Un cookbook Angular des commandes les plus utiles
  • Un extrait de formation

100% gratuit.

Auteur

image auteur
Huber GERARD Fondateur de CodeWise / Ingénieur en informatique huber.gerard@codewise.fr
"Attiré par la pédagogie et le coaching de développeurs webs, j'ai fondé CodeWise en 2020 dans le but de favoriser l'accès à un savoir technique de qualité dans le milieu du dev web. Mon travail : des fois je code, des fois je forme ! Hormis l'IT, je suis un fan de nature, sports en tous genre et yoga."