如何在Angular2中订阅服务上的事件?


112

我知道如何使用EventEmitter引发事件。如果我有这样的组件,我还可以附加一个要调用的方法:

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

当我拥有这样的组件时,一切都很好。我将一些逻辑转移到服务中,并且需要从服务内部引发一个事件。我所做的是:

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

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

我有一个组件需要根据此事件更新一些值,但我似乎无法使其工作。我试过的是:

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

当引发它的服务不是组件时,如何使MyComponent订阅事件?

我正在开启2.0.0-alpha.28

编辑:修改了我的“工作示例”以实际工作,因此可以将重点放在非工作部分;)

示例代码:http : //plnkr.co/edit/m1x62WoCHpKtx0uLNsIv


1
由于服务不应该代表组件发出事件,因为它会将服务与组件紧密耦合,这可能更多是设计问题。例如,如果组件有多个实例,在这种情况下它们都应该发出事件怎么办?
Angular University

@jhadesdev我读过你。我确实重新设计了解决方案,因此该服务不再需要发出结果。我仍然认为某些设计会因能够引发事件而
受益

然后一种方法是让组件创建事件发射器而不是服务,然后将事件发射器作为参数传递给服务
Angular University

Answers:


135

更新:我已经找到了一种更好/更合适的方法来解决此问题,方法是使用BehaviorSubject或Observable而不是EventEmitter。请参阅以下答案:https : //stackoverflow.com/a/35568924/215945

此外,Angular文档现在有一个使用Subject食谱示例


原始/过时/错误的答案:同样,请勿在服务中使用EventEmitter。那是一种反模式。

正在使用beta.1 ... NavService包含EventEmiter。组件导航通过服务发出事件,而组件ObservingComponent订阅事件。

导航服务

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


我已经遵循了,但是它仅适用于类本身。它不适用于另一个类(来自另一个文件)。看来问题出在服务类实例中。是否有使服务类变为静态的方法?
asubanovsky '16

5
@asubanovsky,在插件中,我仅在引导程序(即根组件)级别注入NavService,因此整个应用程序应该只有一个服务实例。您是否将其注入其他地方providers: [NavService]?如果是这样,您将获得该服务的多个实例。
Mark Rajcok '16

非常感谢你。现在正在按我的期望工作。现在,我了解了在引导程序级别和组件级别注入提供程序之间的区别。
asubanovsky '16

帖子已被严重更改为我不再“了解”的状态。为什么在服务.getNavChangeEmitter()上调用返回到EventEmitter的方法不同于直接订阅navChange?
PerHornshøj-Schierbeck16年

@PerHornshøj-Schierbeck,我使用了getNavChangeEmitter()方法来隐藏在服务内部使用EventEmitter的事实。该方法的用户可以假定返回的对象具有一个subscribe()方法。通过使用一种方法,我们可以轻松地将EventEmitter更改为Subject或Observable,这是更好的选择,因为我们现在了解到EventEmitters仅应用于Output属性。正确的方法是使用Subject,BehaviorSubject或Observable,我们通常直接订阅它们,因此我们不再需要getNavChangeEmitter()方法。
Mark Rajcok '16

6

使用Alpha 28,我通过该eventEmitter.toRx().subscribe(..)方法以编程方式完成了对事件发射器的订阅。由于不直观,可能会在将来的版本中进行更改。


1
如果人们偶然发现了这篇文章,也许可以提供一个简单的例子供人们使用?这不是我
拒绝投票吗

2
不知道为什么它被否决了,它肯定有效。简而言之,要订阅事件发射器“点击”,请使用 click.toRx().subscribe(your_callback_function)
Runeboy

我认为可能是因为缺乏有效的例子而被否决了吗?我知道,如果它有一个到pluncker / jsfiddle小提琴的链接,或者甚至有一些代码行可以更好地显示语法,那将是值得赞成和接受的答案;)
PerHornshøj-Schierbeck15年

我遇到异常:ReferenceError:未定义单击@Runeboy您能否为您的解决方案提供plnkr?
本杰明·麦克弗伦

您需要定义一个名为click的EventEmitter才能使其工作
Mazen Elkashef
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.