Angular

Directives et évènements

Bases

Plusieurs types de directives.

@Input(): Parent vers enfant

Doc: officielle

Permet de bind une valeur au composant, depuis le parent.

Dans le contrôleur de l'enfant, la variable visée est attribuée de @Input().

@Input() prend un argument, optionnel, définissant le nom de l'attribut qui sera exposé. Si aucune valeur n'est passée, le nom de la variable est utilisé.

import {Component, Input, OnInit} from '@angular/core';
import {Item} from '../../item.model';

@Component({
  selector: 'app-item',
  templateUrl: './item.component.html',
  styleUrls: ['./item.component.css']
})
export class ItemComponent {
  @Input() value: Item;
}
<app-item
    class="list-group-item"
    *ngFor="let item of items" [item]="item"
    ></app-recipe-item>

@Output(): Enfant vers parent

Doc: officielle

Permet de bind un listener sur un évènement que le composant déclare pouvoir émettre.

Vu qu'il s'agit d'une source d'évènement, la variable attribuée doit être capable d'émettre un évènement. Le type EventEmitter<T> remplit précisément ce travail!

Il suffit ensuite d'appeler la méthode emit(value?: T) de notre variable membre, et si notre composant parent a bindé une méthode sur cet évènement, il obtiendra la valeur.

import {Component, Input, OnInit} from '@angular/core';
import {Item} from '../../item.model';

@Component({
  selector: 'app-item',
  templateUrl: './item.component.html',
  styleUrls: ['./item.component.css']
})
export class ItemComponent {
  @Input() value: Item;
  @Output() selected = new EventEmitter<Item>();

  onSelect() {
      this.selected.emit(this.value);
  }
}
<app-item
    class="list-group-item"
    *ngFor="let item of items" [item]="item"
    (selected)="console.log($event)"
    ></app-recipe-item>

Services

Définition d'un service

Rien de particulier pour définir un service, il suffit de créer une classe.

export class LoggingService {
    private stream = console;

    log(message: string) {
        this.stream.log(message);
    }
}

Injection d'un service

Pour injecter un service dans un contrôleur, il suffit d'ajouter un paramètre constructeur du type de service attendu.

import {Component} from '@angular/core';
import {LoggingService} from '../logging.service';

@Component({
  selector: 'app-new-account',
  templateUrl: './new-account.component.html',
  styleUrls: ['./new-account.component.css'],
  providers: [LoggingService]
})
export class NewAccountComponent {
  constructor(private loggingService: LoggingService) {
  }

  onCreateAccount(accountName: string, accountStatus: string) {
    this.loggingService.logStatusChange(accountStatus);
  }
}

Hiérarchie d'injection

Un composant Angular déclarant un paramètre providers se verra créé une instance dédiée du composant visé.

Tous les enfants de ce composants se verront partager la même instance, sauf si un composant enfant définis aussi un paramètre providers, dans quel cas il aura à son tour une nouvelle instance, partagée entre lui et tous ses enfants.

Le point d'initialisation le plus global est AppModule, qui rendra l'instance disponible à tout angular, y compris à d'autres services.

Le second point d'initialisation "global" est AppComponent, le composant global.

Injection de service dans une classe sans métadonnée

Une classe sans métadonnée (comme un service) ne pourra pas recevoir d'instance injectée. Comment faire, dans ce cas?

Il suffit de marquer la classe comme @Injectable().

Recommandations

Pour accéder à des données d'un service, il vaut mieux récupérer une copie de la liste de données en question, plutôt que récupérer la référence, pour garantir qu'elle ne sera pas modifiée.

Pour remonter l'évènement de changement du contenu de la liste, il faut ajouter un EventEmitter en membre du service, sur lequel la vue s'inscrira pour dynamiquement changer son tableau local.