¿Cómo suscribirse a un evento en un servicio en Angular2?

112

Sé cómo generar un evento con EventEmitter. También puedo adjuntar un método para ser llamado si tengo un componente como este:

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

Cuando tengo un componente como este, todo funciona muy bien. Moví algo de lógica a un servicio y necesito generar un evento desde dentro del Servicio. Lo que hice fue esto:

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

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

Tengo un componente que necesita actualizar algún valor basado en este evento, pero parece que no puedo hacerlo funcionar. Lo que intenté fue esto:

//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
  }
}

¿Cómo hago para que MyComponent se suscriba al evento cuando el servicio que lo genera no es un componente?

Estoy en On 2.0.0-alpha.28

EDITAR: Modifiqué mi "ejemplo de trabajo" para que realmente funcione, por lo que el enfoque se puede poner en la parte que no funciona;)

Código de ejemplo: http://plnkr.co/edit/m1x62WoCHpKtx0uLNsIv

Per Hornshøj-Schierbeck
fuente
1
Podría ser más una cuestión de diseño, ya que un servicio no debería emitir eventos en nombre de un componente, ya que eso une estrechamente el servicio al componente. por ejemplo, ¿qué pasa si hay varias instancias del componente, todas deberían emitir eventos en ese caso?
Angular University
@jhadesdev te leo. Rediseñé la solución para que el servicio ya no necesite emitir el resultado. Sigo pensando que algunos diseños se beneficiarían de poder generar eventos, dependiendo del tipo de "servicio" que sea ...
Per Hornshøj-Schierbeck
una forma podría ser hacer que el componente cree el emisor de eventos en lugar del servicio, y luego pasar el emisor de eventos como un argumento al servicio
Universidad Angular

Respuestas:

135

Actualización : he encontrado una forma mejor / adecuada de resolver este problema utilizando un BehaviorSubject o un Observable en lugar de un EventEmitter. Consulte esta respuesta: https://stackoverflow.com/a/35568924/215945

Además, los documentos de Angular ahora tienen un ejemplo de libro de cocina que usa un Asunto .


Respuesta original / desactualizada / incorrecta: nuevamente, no use un EventEmitter en un servicio . Eso es un anti-patrón.

Usando beta.1 ... NavService contiene el EventEmiter. Component Navigation emite eventos a través del servicio y el componente ObservingComponent se suscribe a los eventos.

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

componentes.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

Mark Rajcok
fuente
He seguido esto, pero solo funciona para la clase en sí. No funciona para otra clase (de otro archivo). Parece que el problema está en la instancia de la clase de servicio. ¿Hay alguna forma de hacer que la clase de servicio se vuelva estática?
asubanovsky
5
@asubanovsky, en el plunker solo inyecto NavService en el nivel de arranque (es decir, componente raíz), por lo que solo debería haber una instancia del servicio para toda la aplicación. ¿Lo estás inyectando en otro lugar providers: [NavService]? Si es así, obtendrá varias instancias del servicio.
Mark Rajcok
Muchas gracias. Ahora está funcionando como esperaba. Ahora entiendo la diferencia entre inyectar los proveedores en el nivel de arranque y en el nivel de componente.
asubanovsky
Esta publicación se cambió seriamente a un estado en el que ya no 'lo entiendo'. ¿Por qué llamar a un método en el servicio .getNavChangeEmitter () que devuelve el EventEmitter es diferente de suscribirse directamente a navChange?
Per Hornshøj-Schierbeck
@ PerHornshøj-Schierbeck, utilicé el método getNavChangeEmitter () para ocultar el hecho de que se estaba utilizando un EventEmitter dentro del servicio. Los usuarios del método podrían asumir que el objeto devuelto tiene un subscribe()método. Al usar un método, podemos cambiar fácilmente EventEmitter a Subject u Observable, lo cual es mucho mejor, ya que ahora hemos aprendido que EventEmitters solo deben usarse para propiedades de salida. La forma correcta es usar un Subject, BehaviorSubject u Observable, y normalmente nos suscribimos directamente a estos, por lo que ya no necesitamos un método getNavChangeEmitter ().
Mark Rajcok
6

Usando alpha 28, logré suscribirme programáticamente a emisores de eventos a través del eventEmitter.toRx().subscribe(..)método. Como no es intuitivo, quizás cambie en una versión futura.

Runeboy
fuente
1
¿Quizás proporcionar un ejemplo simple para que las personas lo usen si se topan con esta publicación? No fui yo quien votó negativamente por cierto;)
Per Hornshøj-Schierbeck
2
No sé por qué fue rechazado, ciertamente funciona. En pocas palabras, para suscribirse a un "clic" de emisor de eventos, utilice click.toRx().subscribe(your_callback_function)
Runeboy
Creo que podría haber sido rechazado porque carece de un ejemplo funcional. Sé que valdría la pena un voto positivo y quizás una respuesta aceptada si tuviera un enlace a un violín pluncker / jsfiddle o incluso algunas líneas de código para mostrar mejor la sintaxis;)
Per Hornshøj-Schierbeck
Obtengo una EXCEPCIÓN: ReferenceError: el clic no está definido @Runeboy ¿Puede proporcionar un plnkr para su solución?
Benjamin McFerren
Necesita definir un EventEmitter llamado haga clic para que funcione
Mazen Elkashef