J'essaie de mettre en œuvre quelque chose comme un modèle de délégation dans Angular. Lorsque l'utilisateur clique sur un nav-item
, je voudrais appeler une fonction qui émet ensuite un événement qui devrait à son tour être géré par un autre composant à l'écoute de l'événement.
Voici le scénario: j'ai un Navigation
composant:
import {Component, Output, EventEmitter} from 'angular2/core';
@Component({
// other properties left out for brevity
events : ['navchange'],
template:`
<div class="nav-item" (click)="selectedNavItem(1)"></div>
`
})
export class Navigation {
@Output() navchange: EventEmitter<number> = new EventEmitter();
selectedNavItem(item: number) {
console.log('selected nav item ' + item);
this.navchange.emit(item)
}
}
Voici la composante d'observation:
export class ObservingComponent {
// How do I observe the event ?
// <----------Observe/Register Event ?-------->
public selectedNavItem(item: number) {
console.log('item index changed!');
}
}
La question clé est de savoir comment faire en sorte que la composante d'observation observe l'événement en question?
Réponses:
Mise à jour 2016-06-27: au lieu d'utiliser Observables, utilisez soit
Un sujet est à la fois un observable (pour que nous y puissions
subscribe()
) et un observateur (pour que nous puissions l'appelernext()
pour émettre une nouvelle valeur). Nous exploitons cette fonctionnalité. Un sujet permet aux valeurs d'être multidiffusées à de nombreux observateurs. Nous n'exploitons pas cette fonctionnalité (nous n'avons qu'un seul Observateur).BehaviorSubject est une variante de Subject. Il a la notion de "valeur actuelle". Nous exploitons ceci: chaque fois que nous créons un ObservingComponent, il obtient automatiquement la valeur actuelle de l'élément de navigation du BehaviorSubject.
Le code ci-dessous et le plongeur utilisent BehaviorSubject.
ReplaySubject est une autre variante de Subject. Si vous voulez attendre qu'une valeur soit réellement produite, utilisez
ReplaySubject(1)
. Alors qu'un BehaviorSubject nécessite une valeur initiale (qui sera fournie immédiatement), ReplaySubject n'en a pas. ReplaySubject fournira toujours la valeur la plus récente, mais comme il n'a pas de valeur initiale requise, le service peut effectuer une opération asynchrone avant de renvoyer sa première valeur. Il se déclenchera toujours immédiatement lors des appels suivants avec la valeur la plus récente. Si vous ne voulez qu'une seule valeur, utilisez-lafirst()
sur l'abonnement. Vous n'avez pas à vous désinscrire si vous utilisezfirst()
.Plunker
Réponse originale qui utilise un Observable: (elle nécessite plus de code et de logique que d'utiliser un BehaviorSubject, donc je ne le recommande pas, mais cela peut être instructif)
Voici donc une implémentation qui utilise un Observable au lieu d'un EventEmitter . Contrairement à mon implémentation EventEmitter, cette implémentation stocke également l'
navItem
élément actuellement sélectionné dans le service, de sorte que lorsqu'un composant d'observation est créé, il peut récupérer la valeur actuelle via un appel APInavItem()
, puis être informé des modifications via l'navChange$
Observable.Plunker
Voir aussi l' exemple du livre de recettes sur l'interaction des composants , qui utilise un
Subject
en plus des observables. Bien que l'exemple soit la «communication parents-enfants», la même technique s'applique aux composants non liés.la source
EventEmitter
n'importe où, sauf pour les sorties. Ils sont en train de réécrire les tutoriels (communication serveur AFAIR) pour ne pas encourager cette pratique._observer
objet de service n'est au moins pas initialisé au momentngOnInit()
de l'appel du composant Navigation.EventEmitter
car il est "chaud" ce qui signifie qu'il est déjà "partagé", il est conçu pour enregistrer la valeur actuelle et enfin il implémente à la fois Observable et Observer qui vous fera économiser au moins cinq lignes de code et deux propriétéssubscribe()
, donc il ne peut pas détecter quand un changement observable. Normalement, il y a un événement asynchrone qui se déclenche (dans mon exemple de code, ce sont les événements de clic de bouton), et le rappel associé appellera next () sur un observable. Mais la détection des modifications s'exécute en raison de l'événement asynchrone et non en raison des modifications observables. Voir aussi les commentaires de Günter: stackoverflow.com/a/36846501/215945ReplaySubject(1)
. ABehaviorSubject
requiert une valeur initiale qui sera fournie immédiatement. LeReplaySubject(1)
fournira toujours la valeur la plus récente, mais n'a pas de valeur initiale requise pour que le service puisse effectuer une opération asynchrone avant de renvoyer sa première valeur, mais se déclenche toujours immédiatement lors des appels suivants avec la dernière valeur. Si vous êtes simplement intéressé par une valeur que vous pouvez utiliserfirst()
sur l'abonnement et que vous n'avez pas à vous désinscrire à la fin, car cela se terminera.Dernières nouvelles: J'ai ajouté une autre réponse qui utilise un Observable plutôt qu'un EventEmitter. Je recommande cette réponse par rapport à celle-ci. Et en fait, l'utilisation d'un EventEmitter dans un service est une mauvaise pratique .
Réponse originale: (ne faites pas cela)
Mettez l'EventEmitter dans un service, ce qui permet à l'ObservingComponent de s'abonner (et de se désinscrire) directement à l'événement:Si vous essayez le Plunker, il y a quelques choses que je n'aime pas dans cette approche:
subscribe()
que le bonthis
soit défini lorsque le rappel est appeléMise à jour: Une alternative qui résout la 2ème puce est d'avoir le ObservingComponent directement souscrire à la
navchange
propriété EventEmitter:Si nous nous abonnons directement, nous n'aurions pas besoin de la
subscribe()
méthode sur NavService.Pour rendre le NavService légèrement plus encapsulé, vous pouvez ajouter une
getNavChangeEmitter()
méthode et l'utiliser:la source
this.subscription = this.navService.subscribe(() => this.selectedNavItem());
et sur abonnement:return this.navchange.subscribe(callback);
Si l'on veut suivre un style de programmation plus réactif, alors le concept de "Tout est un flux" entre en jeu et, par conséquent, utilise Observables pour traiter ces flux aussi souvent que possible.
la source
Vous pouvez utiliser soit:
BehaviorSubject est un type de sujet, un sujet est un type spécial d'observable qui peut agir comme observable et observateur vous pouvez vous abonner à des messages comme tout autre observable et lors de la souscription, il retourne la dernière valeur du sujet émise par la source observable:
Avantage: aucune relation telle qu'une relation parent-enfant requise pour transmettre des données entre les composants.
SERVICE NAV
COMPOSANTE DE NAVIGATION
COMPOSANTE D'OBSERVATION
La deuxième approche est
Event Delegation in upward direction child -> parent
par exemple Répondu par @Ashish Sharma.
la source
Vous devez utiliser le composant Navigation dans le modèle de ObservingComponent (n'oubliez pas d'ajouter un sélecteur au composant Navigation .. navigation-component par ex)
Et implémentez onNavGhange () dans ObservingComponent
Dernière chose .. vous n'avez pas besoin de l'attribut events dans @Componennt
la source
nav-item
mais je veux juste émettre un événement que les autres peuvent observer.vous pouvez utiliser BehaviourSubject comme décrit ci-dessus ou il existe une autre façon:
vous pouvez gérer EventEmitter comme ceci: ajoutez d'abord un sélecteur
Vous pouvez maintenant gérer cet événement comme supposons que observer.component.html est la vue du composant Observer
puis dans ObservingComponent.ts
la source
J'ai trouvé une autre solution pour ce cas sans utiliser ni Reactivex ni les services. J'adore l'API rxjx, mais je pense qu'elle fonctionne mieux lors de la résolution d'une fonction asynchrone et / ou complexe. En l'utilisant de cette façon, c'est assez dépassé pour moi.
Je pense que vous cherchez une émission. Juste ça. Et j'ai découvert cette solution:
Ceci est un exemple de travail complet: https://plnkr.co/edit/xGVuFBOpk2GP0pRBImsE
la source