Ich versuche, so etwas wie ein Delegierungsmuster in Angular zu implementieren. Wenn der Benutzer auf a klickt nav-item
, möchte ich eine Funktion aufrufen, die dann ein Ereignis ausgibt, das wiederum von einer anderen Komponente behandelt werden soll, die auf das Ereignis wartet.
Hier ist das Szenario: Ich habe eine Navigation
Komponente:
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)
}
}
Hier ist die Beobachtungskomponente:
export class ObservingComponent {
// How do I observe the event ?
// <----------Observe/Register Event ?-------->
public selectedNavItem(item: number) {
console.log('item index changed!');
}
}
Die Schlüsselfrage ist, wie ich die Beobachtungskomponente dazu bringe, das betreffende Ereignis zu beobachten.
Antworten:
Update 27.06.2016: Verwenden Sie anstelle von Observables entweder
Ein Subjekt ist sowohl ein Observable (damit wir es erreichen können
subscribe()
) als auch ein Observer (damit wir es aufrufennext()
können, um einen neuen Wert auszugeben). Wir nutzen diese Funktion. Ein Betreff ermöglicht das Multicasting von Werten an viele Beobachter. Wir nutzen diese Funktion nicht aus (wir haben nur einen Beobachter).BehaviorSubject ist eine Variante von Subject. Es hat den Begriff "der aktuelle Wert". Wir nutzen dies aus: Wenn wir eine ObservingComponent erstellen, wird der aktuelle Wert des Navigationselements automatisch vom BehaviorSubject abgerufen.
Der folgende Code und der Plunker verwenden BehaviorSubject.
ReplaySubject ist eine weitere Variante von Subject. Wenn Sie warten möchten, bis ein Wert tatsächlich erzeugt wird, verwenden Sie
ReplaySubject(1)
. Während für ein BehaviorSubject ein Anfangswert erforderlich ist (der sofort bereitgestellt wird), ist dies bei ReplaySubject nicht der Fall. ReplaySubject liefert immer den neuesten Wert. Da jedoch kein erforderlicher Anfangswert vorhanden ist, kann der Dienst eine asynchrone Operation ausführen, bevor der erste Wert zurückgegeben wird. Bei nachfolgenden Aufrufen mit dem neuesten Wert wird es immer noch sofort ausgelöst. Wenn Sie nur einen Wert möchten, verwenden Sie ihnfirst()
für das Abonnement. Sie müssen sich nicht abmelden, wenn Sie verwendenfirst()
.Plunker
Ursprüngliche Antwort, die ein Observable verwendet: (Es erfordert mehr Code und Logik als die Verwendung eines BehaviorSubject, daher empfehle ich es nicht, aber es kann lehrreich sein.)
Hier ist also eine Implementierung, die ein Observable anstelle eines EventEmitter verwendet . Im Gegensatz zu meiner EventEmitter-Implementierung speichert diese Implementierung auch die aktuell
navItem
im Service ausgewählten Elemente , sodass beim Erstellen einer Beobachtungskomponente der aktuelle Wert per API-Aufruf abgerufennavItem()
und über Änderungen übernavChange$
Observable benachrichtigt werden kann .Plunker
Siehe auch das Beispiel des Component Interaction Cookbook ,
Subject
in dem zusätzlich zu Observables ein verwendet wird. Obwohl das Beispiel "Kommunikation zwischen Eltern und Kindern" lautet, gilt dieselbe Technik für nicht verwandte Komponenten.quelle
EventEmitter
nur für Ausgaben empfohlen wird . Sie schreiben derzeit die Tutorials (Server Communication AFAIR) neu, um diese Praxis nicht zu fördern._observer
des Serviceobjekts zum Zeitpunkt desngOnInit()
Aufrufs der Navigationskomponente zumindest nicht initialisiert ist.EventEmitter
weil es "heiß" ist, was bedeutet, dass es bereits "geteilt" ist, den aktuellen Wert speichert und schließlich sowohl Observable als auch Observer implementiert, wodurch Sie mindestens fünf Codezeilen und zwei Eigenschaftensubscribe()
, sodass es nicht erkennen kann, wann sich ein beobachtbares Element ändert. Normalerweise wird ein asynchrones Ereignis ausgelöst (in meinem Beispielcode sind es die Schaltflächenklickereignisse), und der zugehörige Rückruf ruft next () für ein Observable auf. Die Änderungserkennung wird jedoch aufgrund des asynchronen Ereignisses ausgeführt, nicht aufgrund der beobachtbaren Änderung. Siehe auch Günter Kommentare: stackoverflow.com/a/36846501/215945ReplaySubject(1)
. ABehaviorSubject
erfordert einen Anfangswert, der sofort bereitgestellt wird. DerReplaySubject(1)
wird immer den neuesten Wert bereitstellen, es ist jedoch kein Anfangswert erforderlich, sodass der Dienst eine asynchrone Operation ausführen kann, bevor er seinen ersten Wert zurückgibt. Bei nachfolgenden Aufrufen mit dem letzten Wert wird er jedoch sofort ausgelöst. Wenn Sie nur an einem Wert interessiert sind, können Sie ihnfirst()
für das Abonnement verwenden und müssen ihn am Ende nicht abbestellen, da dies abgeschlossen ist.Aktuelle Nachrichten: Ich habe eine weitere Antwort hinzugefügt , die ein Observable anstelle eines EventEmitter verwendet. Ich empfehle diese Antwort über diese. Tatsächlich ist die Verwendung eines EventEmitter in einem Dienst eine schlechte Praxis .
Ursprüngliche Antwort: (Tu das nicht)
Stellen Sie den EventEmitter in einen Dienst, mit dem die ObservingComponent das Ereignis direkt abonnieren (und abbestellen) kann:Wenn Sie das versuchen Plunker, gibt es ein paar Dinge, die ich an diesem Ansatz nicht mag:
subscribe()
damitthis
beim Aufruf des Rückrufs das richtige festgelegt wirdUpdate: Eine Alternative, die das zweite Aufzählungszeichen löst, besteht darin, dass die ObservingComponent die
navchange
EventEmitter-Eigenschaft direkt abonniert :Wenn wir uns direkt
subscribe()
anmelden, benötigen wir die Methode im NavService nicht.Um den NavService etwas gekapselter zu gestalten, können Sie eine
getNavChangeEmitter()
Methode hinzufügen und diese verwenden:quelle
this.subscription = this.navService.subscribe(() => this.selectedNavItem());
und auf abonnieren:return this.navchange.subscribe(callback);
Wenn man einem reaktiveren Programmierstil folgen möchte, kommt definitiv das Konzept "Alles ist ein Stream" ins Spiel und verwendet daher Observables, um diese Streams so oft wie möglich zu verarbeiten.
quelle
Sie können entweder verwenden:
BehaviourSubject ist eine Art von Betreff, ein Betreff ist eine spezielle Art von Observable, die als Observable fungieren kann, und Beobachter, den Sie wie jedes andere Observable abonnieren können, und beim Abonnieren den letzten Wert des Betreffs zurückgeben, der von der beobachtbaren Quelle ausgegeben wird:
Vorteil: Keine Beziehung wie Eltern-Kind-Beziehung erforderlich, um Daten zwischen Komponenten zu übertragen.
NAV SERVICE
NAVIGATIONSKOMPONENTE
BEOBACHTUNG DER KOMPONENTE
Zweiter Ansatz ist
Event Delegation in upward direction child -> parent
zB Beantwortet von @Ashish Sharma.
quelle
Sie müssen die Navigationskomponente in der Vorlage von ObservingComponent verwenden (vergessen Sie nicht, der Navigationskomponente einen Selektor hinzuzufügen . Navigationskomponente zum Beispiel)
Und implementieren Sie onNavGhange () in ObservingComponent
Als letztes benötigen Sie das Ereignisattribut in @Componennt nicht
quelle
nav-item
aber ich möchte nur ein Ereignis ausgeben, das andere beobachten können.Sie können BehaviourSubject wie oben beschrieben verwenden oder es gibt noch einen weiteren Weg:
Sie können mit EventEmitter folgendermaßen umgehen: Fügen Sie zuerst einen Selektor hinzu
Jetzt können Sie dieses Ereignis wie folgt behandeln: Angenommen, Observer.Component.html ist die Ansicht der Observer-Komponente
dann in der ObservingComponent.ts
quelle
Ich habe eine andere Lösung für diesen Fall gefunden, ohne Reactivex oder beide Dienste zu verwenden. Ich liebe die rxjx-API, aber ich denke, sie eignet sich am besten zum Auflösen einer asynchronen und / oder komplexen Funktion. Wenn ich es auf diese Weise benutze, ist es für mich ziemlich übertroffen.
Ich denke, Sie suchen nach einer Sendung. Nur das. Und ich fand diese Lösung heraus:
Dies ist ein voll funktionsfähiges Beispiel: https://plnkr.co/edit/xGVuFBOpk2GP0pRBImsE
quelle