Jakie jest właściwe użycie EventEmitter?

225

Czytałem pytania takie jak Access EventEmitter Service w CustomHttp, w których użytkownik korzysta ze EventEmitter w swojej usłudze, ale w tym komentarzu zasugerowano mu, aby nie korzystał z niego i zamiast tego używał Observable bezpośrednio w swoich usługach.

Przeczytałem również to pytanie, w którym rozwiązanie sugeruje przekazanie EventEmitter dziecku i zasubskrybowanie go.

Moje pytanie brzmi zatem: czy powinienem, czy też nie powinienem subskrybować ręcznie EventEmitter? Jak mam go używać?

Eric Martinez
źródło
2
Możliwe duplikaty przekazania: EventEmitter lub Obserwowalne w Angular2
Günter Zöchbauer
2
Dobra odpowiedź Marka, jak zwykle, ale tak naprawdę nie wyjaśnia, dlaczego to wyjaśniłem. Nie jestem przeciwny jego zamknięciu, ale najpierw chcę jego opinii. @MarkRajcok myśli?
Eric Martinez,
Chciałbym zachować to otwarte (i jestem pewien, że wskażę tutaj ludzi - właśnie zredagowałem swoją drugą odpowiedź, aby wskazać tutaj!). Twoja odpowiedź zawiera sporo dodatkowych informacji. Chcę jednak dwa tytuły pytań ... drugim jest „Jakie jest właściwe użycie EventEmitter?”
Mark Rajcok
@MarkRajcok podoba mi się ten tytuł, ale nie pasowałby do obecnej odpowiedzi, więc postaram się go zaktualizować później, dodam przykłady, jak go używać i jak tego nie robić, więc ma to większy sens. Dziękujemy za opinię :)
Eric Martinez,
@ MarkRajcok edytowany zgodnie z sugestią (y), (skopiuj i wklej sugerowany tytuł, wszystkie podziękowania dla Ciebie).
Eric Martinez,

Odpowiedzi:

342

TL; DR :

Nie, nie subskrybuj ich ręcznie, nie używaj ich w usługach. Używaj ich, jak pokazano w dokumentacji, tylko do emitowania zdarzeń w komponentach. Nie pokonuj abstrakcji kątowej.

Odpowiedź:

Nie, nie powinieneś subskrybować go ręcznie.

EventEmitter to abstrakcja kątowa2, której jedynym celem jest emitowanie zdarzeń w komponentach. Cytując komentarz Roba Wormalda

[...] EventEmitter jest tak naprawdę abstrakcją kątową i powinien być używany prawie wyłącznie do emitowania niestandardowych zdarzeń w komponentach. W przeciwnym razie po prostu użyj Rx, jakby to była jakakolwiek inna biblioteka.

Stwierdzono to bardzo wyraźnie w dokumentacji EventEmitter.

Używaj przez dyrektywy i komponenty do emitowania niestandardowych zdarzeń.

Co jest złego w korzystaniu z niego?

Angular2 nigdy nie zagwarantuje nam, że EventEmitter będzie nadal obserwowalny. Oznacza to przefaktoryzowanie naszego kodu, jeśli się zmieni. Jedynym API, do którego musimy uzyskać dostęp, jest jego emit()metoda. Nigdy nie powinniśmy subskrybować ręcznie EventEmitter.

Wszystkie powyższe stwierdzenia są bardziej jasne w komentarzu Warda Bella (zalecane, aby przeczytać artykuł i odpowiedź na ten komentarz). Cytowanie w celach informacyjnych

NIE licz na to, że EventEmitter będzie nadal obserwowalny!

NIE licz na to, że w przyszłości pojawią się obserwowalni operatorzy!

Zostaną one wkrótce wycofane i prawdopodobnie zostaną usunięte przed wydaniem.

EventEmitter należy używać tylko do wiązania zdarzeń między komponentem podrzędnym i nadrzędnym. Nie subskrybuj tego. Nie wywołuj żadnej z tych metod. Tylko zadzwońeve.emit()

Jego komentarz jest zgodny z komentarzem Roba dawno temu.

Jak więc właściwie go używać?

Po prostu użyj go, aby emitować zdarzenia ze swojego komponentu. Spójrz na następujący przykład.

@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!
    }
}

Jak tego nie używać?

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

Zatrzymaj się tutaj ... już się mylisz ...

Mamy nadzieję, że te dwa proste przykłady wyjaśnią prawidłowe użycie EventEmitter.

Eric Martinez
źródło
1
Co rozumiesz przez: directives : [Child]w definicji komponentu? Wydaje się, że to się nie kompiluje i nie mogę go znaleźć w dokumentacji Angular2.
themathmagician
1
@Eric: Jak nie używać go w twoim przykładzie jest bardzo oczywiste, dlaczego potrzebujemy dekoratora „@Output” w serwisie?
trungk18
1
@themathmagician Po przeprowadzeniu drobnych badań dowiedziałem się , że directivesod tego czasu słowo kluczowe jest przestarzałe. Użyj declarationssłowa kluczowego w, @NgModulejak tutaj lub tutaj
cjsimon,
5
Jakiś komentarz do najnowszej odpowiedzi Toby'ego? Sądzę, że jego odpowiedź powinna być dziś akceptowana.
Arjan
7
@Eric Pisząc tę ​​odpowiedź, napisałeś: „Będą one wkrótce przestarzałe i prawdopodobnie zostaną usunięte przed wydaniem”, cytując Warda Bella. Ale zostało to stwierdzone 2 lata temu i teraz mamy kątowe6. Czy to stwierdzenie obowiązuje jeszcze teraz? W dalszym ciągu widzę w oficjalnym dokumencie, że EventEmitter nadal ma metodę subscribe (), więc myślę, że jeśli Google chciałby przestać opierać EE na tematach Rxjs, już to zrobiłby. Czy uważasz, że twoja oryginalna odpowiedź nadal dobrze pasuje do obecnego stanu Angulara?
Nad G
101

Tak, śmiało i używaj go.

EventEmitterjest publicznym, udokumentowanym typem końcowego API Angular Core. Nie Observablema znaczenia, czy jest on oparty, czy nie ; jeśli jest to udokumentowane emiti subscribemetody odpowiadają temu, czego potrzebujesz, skorzystaj z niego.

Jak stwierdzono również w dokumentach:

Używa Rx.Observable, ale zapewnia adapter, aby działał tak, jak określono tutaj: https://github.com/jhusain/observable-spec

Gdy referencyjna implementacja specyfikacji będzie dostępna, przełącz się na nią.

Chcieli więc Observablepodobnego obiektu, który zachowuje się w określony sposób, zaimplementowali go i upublicznili. Gdyby nie była to tylko wewnętrzna abstrakcja kątowa, której nie należy używać, nie upubliczniliby jej.

Wiele razy warto mieć emiter, który wysyła zdarzenia określonego typu. Jeśli to twój przypadek użycia, idź do niego. Jeśli / kiedy dostępna jest referencyjna implementacja specyfikacji, do której odsyłają, powinna ona zastępować drop-in, tak jak w przypadku każdego innego wypełniacza.

Tylko upewnij się, że generator przekazywany do subscribe()funkcji jest zgodny z połączoną specyfikacją. Zwrócony obiekt ma zagwarantowaną unsubscribemetodę, którą należy wywołać, aby zwolnić wszelkie odniesienia do generatora (jest to obecnie obiekt RxJs,Subscription ale tak naprawdę jest to szczegół implementacji, na którym nie należy polegać).

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();
    }
}

Wszystkie mocno sformułowane przewidywania dotyczące zagłady i mroku wydają się wynikać z jednego komentarza przepełnienia stosu od jednego dewelopera w przedpremierowej wersji Angular 2.

Tobias J
źródło
2
Brzmi rozsądnie - czy ktoś jeszcze chce się z tym zastanowić? subskrybować to w końcu publiczna metoda emitera zdarzeń?
Shawson
Ok, to jest publiczne, więc możemy z niego korzystać. Ale czy istnieje jakiś praktyczny powód, aby użyć EventEmitter zamiast Obserowalnego w tym przykładzie?
Botis,
6
Jesteśmy teraz na Angular v6, a EventEmmiter nie był przestarzały ani usunięty, więc powiedziałbym, że jest bezpieczny w użyciu. Widzę jednak korzyści z nauki korzystania z Observables od RxJS.
David Meza,
Widzę to jako „jeśli używasz EventEmittera poza @Output, nie musisz się spieszyć, zmieniając go na coś innego, ponieważ api to stabilny bankomat”; jeśli masz czas lub przerwy w obsłudze interfejsu API, musisz go zmienić (ale pamiętaj, że zmiana kąta publicznego stabilnego interfejsu API (emitowanie i subskrypcja) nie jest powszechna). Trzymaj się również publicznego interfejsu API, nie korzystaj z metod obiektu lub obserwowalnych, które nie są zdefiniowane w EventEmitter, aby pozostać po bezpieczniejszej stronie
acidghost
1
Założenie, że korzystanie z czegoś jest bezpieczne ze względu na „nie został usunięty z Angulara” lub „dobrze udokumentowane” jest błędne, podobnie jak zaproponowana powyżej zasada braku zasad. Tworzysz projekt nie z powodu przyszłych wersji Angulara. Istnieje wiele niezarządzanych platform na zewnątrz, które nadal działają w Internecie. Wersja frameworka nie powoduje, że programiści pracujący na tych platformach są mniej programiści lub programiści kategorii B.
Claudio Ferraro,
4

Jeśli chcesz mieć interakcję między komponentami, musisz wiedzieć, czym są @Input, @Output, EventEmitter i Tematy.

Jeśli relacja między komponentami jest nadrzędna-podrzędna lub odwrotnie, używamy @input i @output z emiterem zdarzeń.

@output emituje zdarzenie i musisz je emitować za pomocą emitera zdarzeń.

Jeśli nie jest to związek rodzicielski z dzieckiem ... musisz użyć przedmiotów lub za pośrednictwem wspólnej usługi.

Ach
źródło
0

Nie ma: nie i nie: tak. Prawda jest w środku I nie ma powodów, aby się bać z powodu kolejnej wersji Angulara.

Z logicznego punktu widzenia, jeśli masz komponent i chcesz poinformować inne komponenty, że coś się dzieje, zdarzenie powinno zostać uruchomione i można to zrobić w dowolny sposób, który według ciebie (programisty) należy zrobić. Nie widzę powodu, aby go nie używać i nie widzę powodu, dla którego warto go używać za wszelką cenę. Również nazwa EventEmitter sugeruje mi wydarzenie. Zwykle używam go do ważnych wydarzeń mających miejsce w Komponencie. Tworzę usługę, ale tworzę plik usługi w folderze składników. Tak więc mój plik usługi staje się rodzajem Menedżera zdarzeń lub Interfejsu zdarzeń, dzięki czemu mogę szybko zorientować się, które zdarzenie mogę subskrybować w bieżącym składniku.

Wiem ... Może jestem trochę staroświeckim programistą. Ale to nie jest część wzorca projektowania opartego na zdarzeniach, jest to część decyzji dotyczących architektury oprogramowania dla konkretnego projektu.

Niektórzy inni faceci mogą myśleć, że bezpośrednie użycie Observables jest fajne. W takim przypadku przejdź bezpośrednio do Observables. Robiąc to, nie jesteś seryjnym zabójcą. Jeśli nie jesteś programistą psychopatów, do tej pory Program działa, zrób to.

Claudio Ferraro
źródło