Comment souscrire à un événement sur un service dans Angular2?


112

Je sais comment créer un événement avec l'EventEmitter. Je peux également attacher une méthode à appeler si j'ai un composant comme celui-ci:

<component-with-event (myevent)="mymethod($event)" />

Quand j'ai un composant comme celui-ci, tout fonctionne très bien. J'ai déplacé une certaine logique dans un service et je dois déclencher un événement depuis l'intérieur du service. Voici ce que j'ai fait:

export class MyService {
  myevent: EventEmitter = new EventEmitter();

  someMethodThatWillRaiseEvent() {
    this.myevent.next({data: 'fun'});
  }
}

J'ai un composant qui doit mettre à jour une certaine valeur en fonction de cet événement, mais je n'arrive pas à le faire fonctionner. Voici ce que j'ai essayé:

//Annotations...
export class MyComponent {
  constructor(myService: MyService) {
    //myService is injected properly and i already use methods/shared data on this.
    myService.myevent.on(... // 'on' is not a method <-- not working
    myService.myevent.subscribe(.. // subscribe is not a method <-- not working
  }
}

Comment puis-je faire en sorte que MyComponent s'abonne à l'événement lorsque le service qui le déclenche n'est pas un composant?

Je suis sur 2.0.0-alpha.28

EDIT: J'ai modifié mon "exemple de travail" pour qu'il fonctionne réellement, afin que l'accent puisse être mis sur la partie qui ne fonctionne pas;)

Exemple de code: http://plnkr.co/edit/m1x62WoCHpKtx0uLNsIv


1
Il pourrait s'agir davantage d'une question de conception en tant que service qui ne devrait pas émettre d'événements au nom d'un composant, car cela couple étroitement le service au composant. Par exemple, que se passe-t-il s'il existe plusieurs instances du composant, devraient-elles toutes émettre des événements dans ce cas?
Angular University

@jhadesdev je vous ai lu. J'ai repensé la solution pour que le service n'ait plus besoin d'émettre le résultat. Je pense toujours que certaines conceptions gagneraient à être en mesure de déclencher des événements - selon le type de "service" dont il s'agit ...
Per Hornshøj-Schierbeck

une façon pourrait alors être de demander au composant de créer l'émetteur d'événements au lieu du service, puis de transmettre l'émetteur d'événements en tant qu'argument au service
Angular University

Réponses:


135

Mise à jour : J'ai trouvé un moyen meilleur / approprié de résoudre ce problème en utilisant un BehaviorSubject ou un Observable plutôt qu'un EventEmitter. Veuillez consulter cette réponse: https://stackoverflow.com/a/35568924/215945

En outre, les documents Angular ont maintenant un exemple de livre de recettes qui utilise un sujet .


Réponse originale / obsolète / fausse: encore une fois, n'utilisez pas un EventEmitter dans un service . C'est un anti-modèle.

Utilisation de beta.1 ... NavService contient EventEmiter. Component Navigation émet des événements via le service et le composant ObservingComponent s'abonne aux événements.

nav.service.ts

import {EventEmitter} from 'angular2/core';
export class NavService {
  navchange: EventEmitter<number> = new EventEmitter();
  constructor() {}
  emitNavChangeEvent(number) {
    this.navchange.emit(number);
  }
  getNavChangeEmitter() {
    return this.navchange;
  }
}

components.ts

import {Component} from 'angular2/core';
import {NavService} from '../services/NavService';

@Component({
  selector: 'obs-comp',
  template: `obs component, item: {{item}}`
})
export class ObservingComponent {
  item: number = 0;
  subscription: any;
  constructor(private navService:NavService) {}
  ngOnInit() {
    this.subscription = this.navService.getNavChangeEmitter()
      .subscribe(item => this.selectedNavItem(item));
  }
  selectedNavItem(item: number) {
    this.item = item;
  }
  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

@Component({
  selector: 'my-nav',
  template:`
    <div class="nav-item" (click)="selectedNavItem(1)">nav 1 (click me)</div>
    <div class="nav-item" (click)="selectedNavItem(2)">nav 2 (click me)</div>
  `,
})
export class Navigation {
  item = 1;
  constructor(private navService:NavService) {}
  selectedNavItem(item: number) {
    console.log('selected nav item ' + item);
    this.navService.emitNavChangeEvent(item);
  }
}

Plunker


J'ai suivi cela, mais cela ne fonctionne que pour la classe elle-même. Cela ne fonctionne pas pour une autre classe (à partir d'un autre fichier). Il semble que le problème se situe dans l'instance de classe de service. Est-il possible de rendre la classe de service statique?
asubanovsky

5
@asubanovsky, dans le plunker, je n'injecte NavService qu'au niveau du bootstrap (c'est-à-dire du composant racine), il ne devrait donc y avoir qu'une seule instance du service pour l'ensemble de l'application. L'injectez-vous ailleurs providers: [NavService]? Si tel est le cas, vous obtiendrez plusieurs instances du service.
Mark Rajcok

Merci beaucoup. Cela fonctionne maintenant selon mes attentes. Maintenant, je comprends la différence entre l'injection des fournisseurs au niveau du bootstrap et au niveau des composants.
asubanovsky

Ce message a été sérieusement changé pour devenir un état où je ne «comprends plus». Pourquoi l'appel d'un mehtod sur le service .getNavChangeEmitter () qui renvoie l'EventEmitter est-il différent de l'abonnement direct à navChange?
Per Hornshøj-Schierbeck

@ PerHornshøj-Schierbeck, j'ai utilisé la méthode getNavChangeEmitter () pour cacher le fait qu'un EventEmitter était utilisé dans le service. Les utilisateurs de la méthode peuvent supposer que l'objet retourné a une subscribe()méthode. En utilisant une méthode, nous pouvons facilement changer l'EventEmitter en Subject ou en Observable, ce qui est bien mieux, car nous avons maintenant appris que EventEmitters ne doit être utilisé que pour les propriétés de sortie. La bonne façon est d'utiliser un Subject, BehaviorSubject ou Observable, et nous y souscrivons normalement directement, donc nous n'avons plus besoin d'une méthode getNavChangeEmitter ().
Mark Rajcok

6

En utilisant alpha 28, j'ai réussi à m'abonner par programmation à des émetteurs d'événements via la eventEmitter.toRx().subscribe(..)méthode. Comme ce n'est pas intuitif, cela pourrait peut-être changer dans une prochaine version.


1
peut-être fournir un exemple simple aux gens à utiliser s'ils tombent sur ce message? Ce n'était pas moi qui votais contre;)
Per Hornshøj-Schierbeck

2
Je ne sais pas pourquoi il a été voté à la baisse, cela fonctionne certainement. En termes simples, pour vous abonner à un émetteur d'événement "cliquez", utilisez click.toRx().subscribe(your_callback_function)
Runeboy

Je pense qu'il a peut-être été rejeté parce qu'il manque un exemple concret? Je sais que cela vaudrait un vote positif et peut-être une réponse acceptée s'il avait un lien vers un violon pluncker / jsfiddle ou même quelques lignes de code pour mieux afficher la syntaxe;)
Per Hornshøj-Schierbeck

J'obtiens EXCEPTION: ReferenceError: le clic n'est pas défini @Runeboy Pouvez-vous s'il vous plaît fournir un plnkr pour votre solution?
Benjamin McFerren

Vous devez définir un EventEmitter appelé click pour qu'il fonctionne
Mazen Elkashef
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.