Quelle est la bonne utilisation d'un EventEmitter?

224

J'ai lu des questions comme Access EventEmitter Service dans CustomHttp où l'utilisateur utilise EventEmitter dans son service, mais il a été suggéré dans ce commentaire de ne pas l'utiliser et d'utiliser à la place Observables directement dans ses services.

J'ai également lu cette question où la solution suggère de passer l'EventEmitter à l'enfant et de s'y abonner.

Ma question est alors: dois-je ou ne dois-je pas m'abonner manuellement à un EventEmitter? Comment dois-je l'utiliser?

Eric Martinez
la source
2
Copie
Günter Zöchbauer
2
Bonne réponse de Mark, comme d'habitude, mais en fait il n'explique pas pourquoi j'ai expliqué. Je ne suis pas contre sa fermeture, mais je veux d'abord son avis. @MarkRajcok pensées?
Eric Martinez
Je voudrais garder cela ouvert (et je suis sûr que je vais pointer les gens ici - je viens de modifier mon autre réponse pour pointer ici!). Votre réponse contient de nombreuses informations supplémentaires. Je veux cependant deux titres de question ... l'autre est "Quelle est la bonne utilisation d'un EventEmitter?"
Mark Rajcok
@MarkRajcok j'aime ce titre mais il ne correspondrait pas à la réponse actuelle, donc je m'assurerai de le mettre à jour plus tard, d'ajouter des exemples sur la façon de l'utiliser et comment ne pas le faire, donc cela a plus de sens. Merci pour vos commentaires :)
Eric Martinez
@MarkRajcok édité comme suggéré (y), (copiez et collez le titre suggéré, tous les crédits pour vous).
Eric Martinez

Réponses:

342

TL; DR :

Non, ne vous y abonnez pas manuellement, ne les utilisez pas dans les services. Utilisez-les comme indiqué dans la documentation uniquement pour émettre des événements dans les composants. Ne battez pas l'abstraction angulaire.

Répondre:

Non, vous ne devez pas vous y abonner manuellement.

EventEmitter est une abstraction angular2 et son seul but est d'émettre des événements dans les composants. Citant un commentaire de Rob Wormald

[...] EventEmitter est vraiment une abstraction angulaire, et devrait être utilisé à peu près uniquement pour émettre des événements personnalisés dans les composants. Sinon, utilisez simplement Rx comme s'il s'agissait d'une autre bibliothèque.

Ceci est indiqué très clairement dans la documentation d'EventEmitter.

Utilisez des directives et des composants pour émettre des événements personnalisés.

Quel est le problème de l'utiliser?

Angular2 ne nous garantira jamais qu'EventEmitter continuera d'être un observable. Cela signifie donc refactoriser notre code s'il change. La seule API à laquelle nous devons accéder est sa emit()méthode. Nous ne devons jamais souscrire manuellement à un EventEmitter.

Tout ce qui précède est plus clair dans ce commentaire de Ward Bell (recommandé de lire l'article et la réponse à ce commentaire). Citation pour référence

Ne comptez PAS sur EventEmitter pour continuer à être observable!

Ne comptez PAS sur la présence future de ces opérateurs observables!

Ceux-ci seront bientôt obsolètes et probablement supprimés avant la publication.

Utilisez EventEmitter uniquement pour la liaison d'événements entre un composant enfant et parent. Ne vous y abonnez pas. N'appelez aucune de ces méthodes. Appeler seulementeve.emit()

Son commentaire est conforme au commentaire de Rob il y a longtemps.

Alors, comment l'utiliser correctement?

Utilisez-le simplement pour émettre des événements à partir de votre composant. Jetez un œil à l'exemple suivant.

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

Comment ne pas l'utiliser?

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

Arrêtez-vous là ... vous avez déjà tort ...

Espérons que ces deux exemples simples clarifieront l'utilisation appropriée d'EventEmitter.

Eric Martinez
la source
1
Qu'entendez-vous par: directives : [Child]dans la définition du composant? Cela ne semble pas compiler, et je ne peux pas le trouver décrit dans la documentation Angular2.
themathmagicien
1
@Eric: La façon de ne pas l'utiliser dans votre exemple est très évidente, pourquoi avons-nous besoin d'un décorateur '@Output' dans un service?
trungk18
1
@themathmagician Après un peu de recherche, j'ai trouvé ici que le directivesmot - clé est depuis obsolète. Utilisez le declarationsmot - clé @NgModulecomme
indiqué
5
Un commentaire sur la réponse la plus récente de Toby? Je suppose que sa réponse devrait être acceptée de nos jours.
Arjan
7
@Eric Lorsque vous avez écrit cette réponse, vous avez écrit: "Ceux-ci seront bientôt obsolètes et probablement supprimés avant la publication", citant Ward Bell. Mais cela a été dit il y a 2 ans et maintenant nous avons angulaire6. Cette déclaration s'applique-t-elle encore maintenant? Je continue de voir dans le document officiel qu'EventEmitter a toujours la méthode subscribe (), donc je pense que si Google voulait arrêter de baser EE sur des sujets Rxjs, il l'aurait déjà fait. Pensez-vous donc que votre réponse originale correspond toujours bien à l'état actuel d'Angular?
Nad G
100

Oui, allez-y et utilisez-le.

EventEmitterest un type public et documenté dans l'API Angular Core finale. Qu'elle soit fondée Observableou non n'a pas d'importance; si sa documentation emitet ses subscribeméthodes conviennent à vos besoins, allez-y et utilisez-les.

Comme indiqué également dans les documents:

Utilise Rx.Observable mais fournit un adaptateur pour le faire fonctionner comme spécifié ici: https://github.com/jhusain/observable-spec

Une fois qu'une implémentation de référence de la spécification est disponible, passez à celle-ci.

Ils voulaient donc un Observableobjet similaire qui se comporte d'une certaine manière, ils l'ont mis en œuvre et l'ont rendu public. Si ce n'était qu'une abstraction angulaire interne qui ne devrait pas être utilisée, ils ne l'auraient pas rendue publique.

Il y a de nombreuses fois où il est utile d'avoir un émetteur qui envoie des événements d'un type spécifique. Si c'est votre cas d'utilisation, allez-y. Si / quand une implémentation de référence de la spécification à laquelle ils sont liés est disponible, elle devrait être un remplacement direct, comme avec tout autre polyfill.

Assurez-vous simplement que le générateur que vous passez à la subscribe()fonction suit la spécification liée. L'objet retourné est garanti d'avoir une unsubscribeméthode qui devrait être appelée pour libérer toutes les références au générateur (c'est actuellement un objet RxJsSubscription mais c'est en effet un détail d'implémentation dont il ne faut pas dépendre).

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

Toutes les prédictions de malheur et de tristesse fortement formulées semblent provenir d'un seul commentaire Stack Overflow d'un développeur unique sur une version préliminaire d'Angular 2.

Tobias J
la source
2
Cela semble raisonnable - quelqu'un d'autre veut peser là-dessus? s'abonner est une méthode publique sur un émetteur d'événement après tout?
Shawson
Ok, c'est public, nous sommes donc libres de l'utiliser. Mais y a-t-il une raison pratique d'utiliser EventEmitter sur Observable dans cet exemple?
Botis
6
Nous sommes maintenant sur Angular v6 et EventEmmiter n'a pas été déconseillé ou supprimé, donc je dirais qu'il est sûr à utiliser. Cependant, je vois des avantages à apprendre à utiliser Observables de RxJS.
David Meza
Je le vois comme "si vous utilisez EventEmitter en dehors de @Output, pas besoin de le précipiter pour le changer car l'api est un ATM stable"; si vous avez le temps ou que l'api se casse, vous devrez le changer (mais gardez à l'esprit que pour changer angulaire une api publique stable (émettre et souscrire) n'est pas courante). Restez également sur l'
API publique
1
L'hypothèse selon laquelle quelque chose est sûr à utiliser car «n'a pas été retiré d'Angular» ou «bien documenté» est erronée ainsi que la politique nono proposée ci-dessus. Vous créez un projet pas pour la raison des futures versions d'angular. Il existe de nombreuses plates-formes non maintenues à l'extérieur qui fonctionnent toujours sur le Web. La version du framework ne fait pas des développeurs qui travaillaient sur ces plateformes moins des développeurs ou des développeurs de catégorie B.
Claudio Ferraro
4

Lorsque vous souhaitez avoir une interaction entre composants, vous devez savoir ce que sont @Input, @Output, EventEmitter et Subjects.

Si la relation entre les composants est parent-enfant ou vice versa, nous utilisons @input & @output avec un émetteur d'événement.

@output émet un événement et vous devez émettre à l'aide de l'émetteur d'événements.

Si ce n'est pas une relation parent-enfant .. alors vous devez utiliser des sujets ou via un service commun.

Akhil
la source
0

Il n'y a pas: nono et non: oui oui. La vérité est au milieu Et aucune raison d'avoir peur à cause de la prochaine version d'Angular.

D'un point de vue logique, si vous avez un composant et que vous souhaitez informer d'autres composants que quelque chose se produit, un événement doit être déclenché et cela peut être fait de la manière que vous (développeur) pensez que cela devrait être fait. Je ne vois pas la raison pour laquelle ne pas l'utiliser et je ne vois pas la raison pour laquelle l'utiliser à tout prix. Le nom EventEmitter me suggère également qu'un événement se produise. Je l'utilise généralement pour les événements importants qui se produisent dans le composant. Je crée le service mais crée le fichier de service dans le dossier des composants. Ainsi, mon fichier de service devient une sorte de gestionnaire d'événements ou une interface d'événements, afin que je puisse comprendre à quel événement je peux m'abonner sur le composant actuel.

Je sais ... Je suis peut-être un peu un développeur à l'ancienne. Mais cela ne fait pas partie du modèle de développement basé sur les événements, cela fait partie des décisions d'architecture logicielle de votre projet particulier.

Certains autres gars peuvent penser que l'utilisation directe d'Observables est cool. Dans ce cas, allez directement avec Observables. Vous n'êtes pas un tueur en série qui fait ça. Sauf si vous êtes un développeur psychopathe, jusqu'à présent le programme fonctionne, faites-le.

Claudio Ferraro
la source