Como se inscrever em um evento em um serviço no Angular2?


112

Eu sei como gerar um evento com o EventEmitter. Também posso anexar um método a ser chamado se tiver um componente como este:

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

Quando tenho um componente como esse, tudo funciona muito bem. Mudei alguma lógica para um serviço e preciso gerar um evento de dentro do serviço. O que eu fiz foi isso:

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

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

Eu tenho um componente que precisa atualizar algum valor com base neste evento, mas não consigo fazer funcionar. O que eu tentei foi isso:

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

Como faço para que MyComponent se inscreva no evento quando o serviço que o gera não é um componente?

Estou em 2.0.0-alpha.28

EDIT: Modifiquei meu "exemplo de trabalho" para realmente funcionar, então o foco pode ser colocado na parte que não está funcionando;)

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


1
Pode ser mais uma questão de design, pois um serviço não deve emitir eventos em nome de um componente, pois isso acopla fortemente o serviço ao componente. por exemplo, e se houver várias instâncias do componente, todas devem emitir eventos nesse caso?
Angular University

@jhadesdev eu li você. Redesenhei a solução para que o serviço não precise mais emitir o resultado. Eu ainda acho que alguns projetos se beneficiariam por serem capazes de gerar eventos - dependendo do tipo de "serviço" que seja ...
Por Hornshøj-Schierbeck

uma maneira, então, poderia ser fazer com que o componente criasse o emissor de evento em vez do serviço e, em seguida, passasse o emissor de evento como um argumento para o serviço
Angular University

Respostas:


135

Update : Eu encontrei uma maneira melhor / adequada de resolver este problema usando um BehaviorSubject ou um Observable em vez de um EventEmitter. Veja esta resposta: https://stackoverflow.com/a/35568924/215945

Além disso, os documentos Angular agora têm um exemplo de livro de receitas que usa um Assunto .


Resposta original / desatualizada / errada: novamente, não use um EventEmitter em um serviço . Isso é um anti-padrão.

Usando beta.1 ... NavService contém o EventEmiter. O Component Navigation emite eventos por meio do serviço e o componente ObservingComponent se inscreve nos 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;
  }
}

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


Eu acompanhei isso, mas só funciona para a classe em si. Não está funcionando para outra classe (de outro arquivo). Parece que o problema está na instância da classe de serviço. Existe alguma maneira de tornar a classe de serviço estática?
asubanovsky de

5
@asubanovsky, no plunker eu apenas injeto NavService no nível de bootstrap (ou seja, componente raiz), então deve haver apenas uma instância do serviço para todo o aplicativo. Você está injetando em outro lugar providers: [NavService]? Nesse caso, você obterá várias instâncias do serviço.
Mark Rajcok

Muito obrigado. Agora está funcionando como minha expectativa. Agora eu entendo a diferença entre injetar os provedores no nível de bootstrap e no nível do componente.
asubanovsky

Esta postagem foi seriamente alterada para um estado em que eu não "entendo" mais. Por que chamar um mehtod no serviço .getNavChangeEmitter () que retorna o EventEmitter é diferente de se inscrever diretamente no navChange?
Per Hornshøj-Schierbeck

@ PerHornshøj-Schierbeck, usei o método getNavChangeEmitter () para esconder o fato de que um EventEmitter estava sendo usado dentro do serviço. Os usuários do método podem presumir que o objeto retornado possui um subscribe()método. Usando um método, podemos facilmente alterar o EventEmitter para um Assunto ou Observable, o que é muito melhor, pois agora aprendemos que EventEmitters deve ser usado apenas para propriedades de saída. A maneira correta é usar um Assunto, BehaviorSubject ou Observable, e normalmente os assinamos diretamente, portanto, não precisamos mais de um método getNavChangeEmitter ().
Mark Rajcok

6

Usando o alfa 28, consegui inscrever-me programaticamente em emissores de eventos por meio do eventEmitter.toRx().subscribe(..)método. Como não é intuitivo, pode ser alterado em uma versão futura.


1
talvez forneça um exemplo simples para as pessoas usarem se toparem com esta postagem? A propósito, não fui eu quem votou contra;)
Per Hornshøj-Schierbeck

2
Não sei por que foi rejeitado, certamente funciona. Simplificando, para se inscrever em um "clique" de emissor de evento, use click.toRx().subscribe(your_callback_function)
Runeboy

Eu acho que pode ter sido rejeitado porque falta um exemplo funcional. Eu sei que valeria a pena um voto positivo e talvez uma resposta aceita se tivesse um link para um pluncker / jsfiddle fiddle ou mesmo algumas linhas de código para exibir melhor a sintaxe;)
Por Hornshøj-Schierbeck

Estou recebendo EXCEÇÃO: ReferenceError: o clique não está definido @Runeboy Você pode fornecer um plnkr para sua solução?
Benjamin McFerren

Você precisa definir um EventEmitter chamado click para que funcione
Mazen Elkashef
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.