Il n'y a pas d'équivalent à $scope.emit()
o $scope.broadcast()
d'AngularJS. EventEmitter à l'intérieur d'un composant s'en rapproche, mais comme vous l'avez mentionné, il n'émettra un événement que vers le composant parent immédiat.
Dans Angular, il existe d'autres alternatives que je vais essayer d'expliquer ci-dessous.
Les liaisons @Input() permettent de connecter le modèle d'application dans un graphe d'objets dirigé (de la racine aux feuilles). Le comportement par défaut de la stratégie de détection des changements d'un composant consiste à propager tous les changements apportés à un modèle d'application pour tous les bindings de tout composant connecté.
Aside : Il existe deux types de modèles : Les modèles de vue et les modèles d'application. Un modèle d'application est relié par des liaisons @Input(). Un modèle de vue est simplement une propriété de composant (non décorée avec @Input()) qui est liée dans le modèle du composant.
Pour répondre à vos questions :
Que faire si j'ai besoin de communiquer entre les composants d'une fratrie ?
-
Modèle d'application partagée : Les frères et sœurs peuvent communiquer par le biais d'un modèle d'application partagé (tout comme angular 1). Par exemple, lorsqu'un membre de la fratrie apporte une modification à un modèle, l'autre membre de la fratrie qui a des liens avec le même modèle est automatiquement mis à jour.
-
Événements liés aux composants : Les composants enfants peuvent émettre un événement vers le composant parent en utilisant des liaisons @Output(). Le composant parent peut gérer l'événement, et manipuler le modèle de l'application ou son propre modèle de vue. Les modifications apportées au modèle d'application sont automatiquement propagées à tous les composants qui se lient directement ou indirectement au même modèle.
-
Événements de service : Les composants peuvent s'abonner à des événements de service. Par exemple, deux composants frères et sœurs peuvent s'abonner au même événement de service et répondre en modifiant leurs modèles respectifs. Plus d'informations à ce sujet ci-dessous.
Comment puis-je communiquer entre un composant racine et un composant imbriqué à plusieurs niveaux de profondeur ?
-
Modèle d'application partagée : Le modèle d'application peut être transmis du composant Root vers les sous-composants profondément imbriqués par le biais de liaisons @Input(). Les modifications apportées à un modèle à partir de n'importe quel composant se propagent automatiquement à tous les composants qui partagent le même modèle.
-
Événements de service : Vous pouvez également déplacer l'EventEmitter vers un service partagé, ce qui permet à tout composant d'injecter le service et de s'abonner à l'événement. De cette façon, un composant racine peut appeler une méthode du service (généralement en modifiant le modèle), qui à son tour émet un événement. Plusieurs couches plus bas, un composant petit-enfant qui a également injecté le service et souscrit au même événement, peut le traiter. Tout gestionnaire d'événement qui modifie un modèle d'application partagé se propage automatiquement à tous les composants qui en dépendent. C'est probablement l'équivalent le plus proche de
$scope.broadcast()
d'Angular 1. La section suivante décrit cette idée plus en détail.
Exemple d'un service observable qui utilise les événements du service pour propager les changements.
Voici un exemple de service observable qui utilise les événements de service pour propager les changements. Lorsqu'un TodoItem est ajouté, le service émet un événement notifiant les abonnés de ses composants.
export class TodoItem {
constructor(public name: string, public done: boolean) {
}
}
export class TodoService {
public itemAdded$: EventEmitter<TodoItem>;
private todoList: TodoItem[] = [];
constructor() {
this.itemAdded$ = new EventEmitter();
}
public list(): TodoItem[] {
return this.todoList;
}
public add(item: TodoItem): void {
this.todoList.push(item);
this.itemAdded$.emit(item);
}
}
Voici comment un composant Root s'abonnerait à l'événement :
export class RootComponent {
private addedItem: TodoItem;
constructor(todoService: TodoService) {
todoService.itemAdded$.subscribe(item => this.onItemAdded(item));
}
private onItemAdded(item: TodoItem): void {
// do something with added item
this.addedItem = item;
}
}
Un composant enfant imbriqué à plusieurs niveaux s'abonnerait à l'événement de la même manière :
export class GrandChildComponent {
private addedItem: TodoItem;
constructor(todoService: TodoService) {
todoService.itemAdded$.subscribe(item => this.onItemAdded(item));
}
private onItemAdded(item: TodoItem): void {
// do something with added item
this.addedItem = item;
}
}
Voici le composant qui appelle le service pour déclencher l'événement (il peut se trouver n'importe où dans l'arbre des composants) :
@Component({
selector: 'todo-list',
template: `
<ul>
<li *ngFor="#item of model"> {{ item.name }}
</li>
</ul>
<br />
Add Item <input type="text" #txt /> <button (click)="add(txt.value); txt.value='';">Add</button>
`
})
export class TriggeringComponent{
private model: TodoItem[];
constructor(private todoService: TodoService) {
this.model = todoService.list();
}
add(value: string) {
this.todoService.add(new TodoItem(value, false));
}
}
Référence : Détection des changements en Angular
2 votes
J'avais une question similaire concernant la création d'un composant de dialogue auquel on pouvait accéder à partir de n'importe quel point du domaine : stackoverflow.com/questions/34572539/ En gros, une solution consiste à placer un émetteur d'événements dans un service
1 votes
Voici mon implémentation d'un tel service en utilisant RXJS qui permet d'obtenir les nièmes dernières valeurs sur abonnement. stackoverflow.com/questions/46027693/