Interactions avec les composants enfants

Objectifs de la partie

  • Apprendre à faire passer des données entre les composants

Contexte

Bien souvent, nous avons besoin de faire passer des informations entre un composant et ses enfants.

Méthode

Nous avons vu qu'un composant Angular peut être composé d'autre composants. Cette approche est intéressante par nature mais le devient encore plus dès lors que l'on peut échanger des informations entre un parent et son enfant.

Passage d'informations à l'enfant @Input.

Le décorateur @Input permet de déclarer qu'un composant attend des informations en entrée. Il se place sur une propriété du composant et indique que celle-ci est bindée sur une propriété de son parent. Le parent doit renseigner cette propriété sous forme d'attribut du sélecteur. Une modification de la propriété d'origine dans le parent déclenche une modification de la propriété bindée de l'enfant.

Admettons que l'on désire afficher des news sur l'accueil de notre application. Nous créons donc un composant news-item représentant l'affichage d'une news. Le composant app va donc utiliser ce composant news-item (créant ainsi une relation parent-enfant). C'est bien le composant app qui va récupérer et contenir la liste de news et leur détail. Il va donc falloir que celui-ci passe les informations sur chacune des news au composant news-item.

1
@Component({
2
  selector: 'sty-news-item',
3
  templateUrl: './news-item.component.html'
4
})
5
export class NewsItemComponent {
6
  @Input()
7
  public title: string;
8
  @Input()
9
  public content: string;
10
}

Le composant news-item possède deux propriétés : title et content. Ces propriétés sont annotées @Input, cela indique qu'elles sont attendues en entrée du composant. Le template d'une news est relativement simple et contient juste l'affichage du titre et du contenu :

1
<h3>{{ title }}</h3>
2
<p>{{ content }}</p>

Pour passer les paramètres title et news, le composant app utilise la syntaxe suivante :

1
<p>Flash info, dernières nouvelles :</p>
2
<div *ngFor="let news of newsList">
3
  {{ news.title }}
4
  <sty-news-item title="{{ news.title }}" content="{{ news.content }}"></sty-news-item>
5
</div>

Il parcourt la liste de news qu'il contient grâce à sa propriété newsList. Et pour chaque news, il crée une instance du composant news-item pour afficher ses informations.

Il est aussi tout à fait possible de passer des valeurs fixes en paramètre :

1
<sty-news-item title="Hello" content="World"></sty-news-item>

Passage d'informations au parent @Output.

Un composant enfant peut aussi avoir besoin de passer des informations ou un événement à son parent afin qu'il effectue des actions. Cette organisation du code permet de rendre les composants plus génériques et indépendants. Cette transmission d'informations se fait grâce au décorateur @Output.

Reprenons l'exemple de notre affichage de news. Nous voulons maintenant pouvoir notifier le composant app au clic sur un bouton afin qu'il ajoute un smiley au titre.

1
@Component({
2
  selector: 'sty-news-item',
3
  templateUrl: './news-item.component.html'
4
})
5
export class NewsItemComponent {
6
  @Input()
7
  public title: string;
8
  @Input()
9
  public content: string;
10
11
  @Output()
12
  private smileyClick = new EventEmitter<any>();
13
14
  appendSmiley() {
15
    this.smileyClick.emit(this.title);
16
  }
17
}

Vous remarquerez que @Output s'utilise sur une propriété de type EventEmitter et qu'il faut appeler la méthode .emit() pour déclencher l'événement.

1
<h3>{{ title }}</h3>
2
<p>{{ content }}</p>
3
4
<button (click)="appendSmiley()">Ajouter smiley</button>

Le clic sur le bouton « Ajouter smiley » provoque l'appel à la méthode du news-item, celle-là même qui utilise le .emit() et déclenche l'événement.

1
<p>Flash info, dernières nouvelles :</p>
2
<div *ngFor="let news of newsList">
3
  {{ news.title }}
4
  <sty-news-item title="{{ news.title }}" content="{{ news.content }}" (smileyClick)="appendSmiley($event)"></sty-news-item>
5
</div>

On utilise ensuite (smileyClick)=”<, méthode du parent permettant de traiter l’événement >” pour récupérer et traiter l'événement émis par l'enfant.

1
@Component({
2
  selector: 'sty-root',
3
  templateUrl: './app.component.html',
4
  styleUrls: ['./app.component.scss']
5
})
6
export class AppComponent {
7
  title = 'angular-basics';
8
9
  public newsList = [
10
    new News('Météo - Demain', 'Il fait beau demain.'),
11
    new News('Météo - Apres demain', 'Il fait beau après demain !'),
12
    new News('Attention', 'Problème de contrôle')
13
  ];
14
15
  appendSmiley($title: string) {
16
    this.newsList.find(news => news.title === $title).title += ' :)';
17
  }
18
}

Enfin, la méthode appendSmiley du parent a la charge d'ajouter un smiley à la fin du titre de la news concernée, retrouvée ici grâce à son titre passé par l'enfant dans l'événement.

SyntaxeÀ retenir

  • @Input,

  • @Output,

  • <childComponent attributInput=”valeur” (attributOutput)=”méthode de traitement de l’événement ”>.