Was ist die richtige Verwendung eines EventEmitter?

224

Ich habe Fragen wie Access EventEmitter Service in CustomHttp gelesen, bei denen der Benutzer EventEmitter in seinem Dienst verwendet. In diesem Kommentar wurde ihm jedoch empfohlen , ihn nicht zu verwenden und stattdessen Observables direkt in seinen Diensten zu verwenden.

Ich habe auch diese Frage gelesen , in der die Lösung vorschlägt, den EventEmitter an das Kind zu übergeben und ihn zu abonnieren.

Meine Frage lautet dann: Soll ich einen EventEmitter manuell abonnieren oder nicht? Wie soll ich es benutzen?

Eric Martinez
quelle
2
Mögliches Duplikat der Delegation: EventEmitter oder Observable in Angular2
Günter Zöchbauer
2
Gute Antwort von Mark, wie immer, aber eigentlich erklärt er nicht, warum ich es erklärt habe. Ich bin nicht dagegen, es zu schließen, aber ich möchte zuerst seine Meinung. @ MarkRajcok Gedanken?
Eric Martinez
Ich möchte dies offen halten (und ich bin sicher, ich werde Leute hier zeigen - ich habe gerade meine andere Antwort bearbeitet, um hier zu zeigen!). Ihre Antwort enthält einige zusätzliche Informationen. Ich möchte jedoch zwei Fragentitel ... der andere lautet "Was ist die ordnungsgemäße Verwendung eines EventEmitter?"
Mark Rajcok
@MarkRajcok Ich mag diesen Titel, aber er würde nicht zur aktuellen Antwort passen. Deshalb werde ich ihn später aktualisieren und Beispiele hinzufügen, wie man ihn verwendet und wie man ihn nicht verwendet, damit er sinnvoller ist. Vielen Dank für Ihr Feedback :)
Eric Martinez
@MarkRajcok bearbeitet wie vorgeschlagen (y), (kopiere und füge den vorgeschlagenen Titel ein, alle Credits an dich).
Eric Martinez

Antworten:

342

TL; DR :

Nein, abonnieren Sie sie nicht manuell, verwenden Sie sie nicht in Diensten. Verwenden Sie sie wie in der Dokumentation gezeigt nur, um Ereignisse in Komponenten auszugeben. Besiege nicht die Abstraktion von Angular.

Antworten:

Nein, Sie sollten es nicht manuell abonnieren.

EventEmitter ist eine Angular2-Abstraktion, deren einziger Zweck darin besteht, Ereignisse in Komponenten auszugeben. Zitiert einen Kommentar von Rob Wormald

[...] EventEmitter ist wirklich eine Angular-Abstraktion und sollte so ziemlich nur zum Ausgeben von benutzerdefinierten Ereignissen in Komponenten verwendet werden. Andernfalls verwenden Sie Rx einfach so, als wäre es eine andere Bibliothek.

Dies wird in der Dokumentation von EventEmitter sehr deutlich.

Wird von Anweisungen und Komponenten verwendet, um benutzerdefinierte Ereignisse auszugeben.

Was ist falsch daran?

Angular2 wird uns niemals garantieren, dass EventEmitter weiterhin ein Observable ist. Das bedeutet also, unseren Code zu überarbeiten, wenn er sich ändert. Die einzige API, auf die wir zugreifen müssen, ist ihre emit()Methode. Wir sollten einen EventEmitter niemals manuell abonnieren.

Alle oben genannten Punkte sind in diesem Kommentar von Ward Bell klarer (empfohlen, den Artikel und die Antwort auf diesen Kommentar zu lesen ). Zitat als Referenz

Verlassen Sie sich NICHT darauf, dass EventEmitter weiterhin ein Observable ist!

Verlassen Sie sich NICHT darauf, dass diese Observable-Operatoren in Zukunft da sind!

Diese werden bald veraltet sein und wahrscheinlich vor der Veröffentlichung entfernt.

Verwenden Sie EventEmitter nur für die Ereignisbindung zwischen einer untergeordneten und einer übergeordneten Komponente. Abonnieren Sie es nicht. Rufen Sie keine dieser Methoden auf. Nur anrufeneve.emit()

Sein Kommentar stimmt mit Robs Kommentar vor langer Zeit überein.

Also, wie man es richtig benutzt?

Verwenden Sie es einfach, um Ereignisse von Ihrer Komponente auszusenden. Schauen Sie sich das folgende Beispiel an.

@Component({
    selector : 'child',
    template : `
        <button (click)="sendNotification()">Notify my parent!</button>
    `
})
class Child {
    @Output() notifyParent: EventEmitter<any> = new EventEmitter();
    sendNotification() {
        this.notifyParent.emit('Some value to send to the parent');
    }
}

@Component({
    selector : 'parent',
    template : `
        <child (notifyParent)="getNotification($event)"></child>
    `
})
class Parent {
    getNotification(evt) {
        // Do something with the notification (evt) sent by the child!
    }
}

Wie man es nicht benutzt?

class MyService {
    @Output() myServiceEvent : EventEmitter<any> = new EventEmitter();
}

Halt genau dort an ... du liegst schon falsch ...

Hoffentlich verdeutlichen diese beiden einfachen Beispiele die ordnungsgemäße Verwendung von EventEmitter.

Eric Martinez
quelle
1
Was meinst du mit: directives : [Child]in der Komponentendefinition? Dies scheint nicht zu kompilieren, und ich kann es nicht in der Angular2-Dokumentation beschrieben finden.
Themathmagician
1
@Eric: Wie man es in Ihrem Beispiel nicht verwendet, ist ganz offensichtlich, warum wir in einem Service den Dekorator '@Output' benötigen.
Trungk18
1
@themathmagician Nach ein wenig Recherche habe ich hier festgestellt , dass das directivesKeyword inzwischen veraltet ist. Verwenden Sie das declarationsSchlüsselwort in @NgModulewie hier oder hier angegeben
cjsimon
5
Irgendwelche Kommentare zu Tobys neuerer Antwort? Ich würde vermuten, dass seine Antwort heutzutage die akzeptierte sein sollte.
Arjan
7
@Eric Als Sie diese Antwort geschrieben haben, haben Sie geschrieben: "Diese werden bald veraltet sein und wahrscheinlich vor der Veröffentlichung entfernt", unter Berufung auf Ward Bell. Aber das wurde vor 2 Jahren angegeben und jetzt haben wir eckig6. Gilt diese Aussage jetzt noch? Ich sehe im offiziellen Dokument immer wieder, dass EventEmitter immer noch die subscribe () -Methode hat. Wenn Google also aufhören wollte, EE auf Rxjs Themen zu stützen, würde dies bereits geschehen. Denken Sie also, dass Ihre ursprüngliche Antwort immer noch gut zum aktuellen Status von Angular passt?
Nad G
100

Ja, mach weiter und benutze es.

EventEmitterist ein öffentlicher, dokumentierter Typ in der endgültigen Angular Core-API. Ob es darauf basiert oder nicht, Observableist irrelevant; Wenn die Dokumentation emitund die subscribeMethoden Ihren Anforderungen entsprechen, verwenden Sie sie.

Wie auch in den Dokumenten angegeben:

Verwendet Rx.Observable, stellt jedoch einen Adapter bereit, damit es wie hier angegeben funktioniert: https://github.com/jhusain/observable-spec

Sobald eine Referenzimplementierung der Spezifikation verfügbar ist, wechseln Sie zu dieser.

Also wollten sie ein Observableähnliches Objekt, das sich auf eine bestimmte Weise verhält, sie implementierten es und machten es öffentlich. Wenn es nur eine interne Angular-Abstraktion wäre, die nicht verwendet werden sollte, hätten sie sie nicht öffentlich gemacht.

Es gibt viele Fälle, in denen es nützlich ist, einen Emitter zu haben, der Ereignisse eines bestimmten Typs sendet. Wenn das Ihr Anwendungsfall ist, machen Sie es. Wenn / wenn eine Referenzimplementierung der Spezifikation verfügbar ist, auf die sie verweisen, sollte dies wie bei jeder anderen Polyfüllung ein Drop-In-Ersatz sein.

Stellen Sie nur sicher, dass der Generator, den Sie an die subscribe()Funktion übergeben, der verknüpften Spezifikation entspricht. Das zurückgegebene Objekt verfügt garantiert über eine unsubscribeMethode, die aufgerufen werden sollte, um alle Verweise auf den Generator freizugebenSubscription (dies ist derzeit ein RxJs- Objekt, aber dies ist in der Tat ein Implementierungsdetail, von dem nicht abhängig sein sollte).

export class MyServiceEvent {
    message: string;
    eventId: number;
}

export class MyService {
    public onChange: EventEmitter<MyServiceEvent> = new EventEmitter<MyServiceEvent>();

    public doSomething(message: string) {
        // do something, then...
        this.onChange.emit({message: message, eventId: 42});
    }
}

export class MyConsumer {
    private _serviceSubscription;

    constructor(private service: MyService) {
        this._serviceSubscription = this.service.onChange.subscribe({
            next: (event: MyServiceEvent) => {
                console.log(`Received message #${event.eventId}: ${event.message}`);
            }
        })
    }

    public consume() {
        // do some stuff, then later...

        this.cleanup();
    }

    private cleanup() {
        this._serviceSubscription.unsubscribe();
    }
}

Alle stark formulierten Doom- und Gloom-Vorhersagen scheinen auf einen einzelnen Stack Overflow-Kommentar eines einzelnen Entwicklers zu einer Vorabversion von Angular 2 zurückzuführen zu sein.

Tobias J.
quelle
2
Das klingt vernünftig - möchte noch jemand darüber nachdenken? Abonnieren ist doch eine öffentliche Methode für einen Event-Emitter?
Shawson
Ok, es ist öffentlich, also können wir es frei benutzen. Aber gibt es in diesem Beispiel einen praktischen Grund, EventEmitter über Observable zu verwenden?
Botis
6
Wir sind jetzt auf Angular v6 und EventEmmiter wurde nicht veraltet oder entfernt, daher würde ich sagen, dass die Verwendung sicher ist. Ich sehe jedoch Vorteile beim Erlernen der Verwendung von Observables von RxJS.
David Meza
Ich sehe es als "Wenn Sie EventEmitter außerhalb von @Output verwenden, müssen Sie es nicht schnell in etwas anderes ändern, da die API ein stabiler Geldautomat ist." Wenn Sie Zeit haben oder die API unterbrochen wird, müssen Sie sie ändern (beachten Sie jedoch, dass das Ändern einer öffentlichen stabilen API (Senden und Abonnieren) für Winkel nicht üblich ist). Halten Sie sich auch an die öffentliche API, greifen Sie nicht auf Methoden von Subject oder Observable zu, die nicht in EventEmitter definiert sind, um auf der sichereren Seite zu bleiben
acidghost
1
Die Annahme, dass etwas sicher zu verwenden ist, weil es "nicht aus Angular entfernt" oder "gut dokumentiert" ist, ist ebenso falsch wie die oben vorgeschlagene Nono-Richtlinie. Sie erstellen ein Projekt nicht für zukünftige Versionen von Angular. Es gibt viele nicht gepflegte Plattformen draußen, die noch im Web arbeiten. Die Version des Frameworks macht Entwickler, die auf diesen Plattformen gearbeitet haben, nicht weniger Entwickler oder Entwickler der Kategorie B.
Claudio Ferraro
4

Wenn Sie eine komponentenübergreifende Interaktion wünschen, müssen Sie wissen, was @Input, @Output, EventEmitter und Subjects sind.

Wenn die Beziehung zwischen Komponenten Eltern-Kind ist oder umgekehrt, verwenden wir @input & @output mit Event-Emitter.

@output gibt ein Ereignis aus und Sie müssen es mit dem Ereignisemitter ausgeben.

Wenn es sich nicht um eine Eltern-Kind-Beziehung handelt, müssen Sie Themen oder einen gemeinsamen Dienst verwenden.

Akhil
quelle
0

Es gibt no: nono und no: yesyes. Die Wahrheit liegt in der Mitte Und es gibt keinen Grund, wegen der nächsten Version von Angular Angst zu haben.

Aus logischer Sicht sollte ein Ereignis ausgelöst werden, wenn Sie eine Komponente haben und andere Komponenten darüber informieren möchten, dass etwas passiert, und dies kann auf jede Art und Weise erfolgen, die Sie (Entwickler) für erforderlich halten. Ich sehe keinen Grund, warum ich es nicht benutze, und ich sehe keinen Grund, warum ich es um jeden Preis benutze. Auch der Name EventEmitter deutet darauf hin, dass ein Ereignis stattfindet. Normalerweise verwende ich es für wichtige Ereignisse in der Komponente. Ich erstelle den Service, erstelle aber die Service-Datei im Komponentenordner. So wird meine Servicedatei zu einer Art Ereignismanager oder einer Ereignisschnittstelle, sodass ich auf einen Blick herausfinden kann, welches Ereignis ich für die aktuelle Komponente abonnieren kann.

Ich weiß ... Vielleicht bin ich ein bisschen ein altmodischer Entwickler. Dies ist jedoch nicht Teil des ereignisgesteuerten Entwicklungsmusters, sondern Teil der Entscheidungen zur Softwarearchitektur Ihres speziellen Projekts.

Einige andere Leute denken vielleicht, dass es cool ist, Observables direkt zu verwenden. In diesem Fall fahren Sie direkt mit Observables fort. Du bist kein Serienmörder, der das tut. Wenn Sie kein Psychopath-Entwickler sind, tun Sie es, soweit das Programm funktioniert.

Claudio Ferraro
quelle