angular - 委托(delegate) : EventEmitter or Observable in Angular

标签 angular observer-pattern observable eventemitter event-delegation

我正在尝试在 Angular 中实现类似于委托(delegate)模式的东西。 当用户单击 nav-item 时,我想调用一个函数,该函数然后发出一个事件,该事件又应该由其他监听该事件的组件处理。

场景如下:我有一个 Navigation 组件:

import {Component, Output, EventEmitter} from 'angular2/core';

@Component({
    // other properties left out for brevity
    events : ['navchange'], 
    template:`
      <div class="nav-item" (click)="selectedNavItem(1)"></div>
    `
})

export class Navigation {

    @Output() navchange: EventEmitter<number> = new EventEmitter();

    selectedNavItem(item: number) {
        console.log('selected nav item ' + item);
        this.navchange.emit(item)
    }

}

这是观察组件:

export class ObservingComponent {

  // How do I observe the event ? 
  // <----------Observe/Register Event ?-------->

  public selectedNavItem(item: number) {
    console.log('item index changed!');
  }

}

关键问题是,如何让观察组件观察到有问题的事件?

最佳答案

2016-06-27 更新:不使用 Observables,而是使用其中之一

  • @Abdulrahman 在评论中推荐的 BehaviorSubject,或者
  • ReplaySubject,由@Jason Goemaat 在评论中推荐

A Subject既是一个 Observable(所以我们可以 subscribe() 给它)也是一个 Observer(所以我们可以调用 next() 来发射一个新值)。我们利用此功能。 Subject 允许将值多播给许多观察者。我们不利用这个特性(我们只有一个观察者)。

BehaviorSubject是 Subject 的变体。它具有“当前值”的概念。我们利用了这一点:每当我们创建一个 ObservingComponent 时,它都会自动从 BehaviorSubject 获取当前导航项的值。

下面的代码和 plunker使用 BehaviorSubject。

ReplaySubject是 Subject 的另一种变体。如果要等到实际产生值,请使用 ReplaySubject(1) .而 BehaviorSubject 需要一个初始值(将立即提供),而 ReplaySubject 则不需要。 ReplaySubject 将始终提供最新的值,但由于它没有所需的初始值,因此服务可以在返回第一个值之前执行一些异步操作。它仍然会在具有最新值的后续调用中立即触发。如果您只想要一个值,请使用 first()在订阅上。如果您使用 first(),则无需退订.

import {Injectable}      from '@angular/core'
import {BehaviorSubject} from 'rxjs/BehaviorSubject';

@Injectable()
export class NavService {
  // Observable navItem source
  private _navItemSource = new BehaviorSubject<number>(0);
  // Observable navItem stream
  navItem$ = this._navItemSource.asObservable();
  // service command
  changeNav(number) {
    this._navItemSource.next(number);
  }
}
import {Component}    from '@angular/core';
import {NavService}   from './nav.service';
import {Subscription} from 'rxjs/Subscription';

@Component({
  selector: 'obs-comp',
  template: `obs component, item: {{item}}`
})
export class ObservingComponent {
  item: number;
  subscription:Subscription;
  constructor(private _navService:NavService) {}
  ngOnInit() {
    this.subscription = this._navService.navItem$
       .subscribe(item => this.item = item)
  }
  ngOnDestroy() {
    // prevent memory leak when component is destroyed
    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.changeNav(item);
  }
}

Plunker


使用 Observable 的原始答案:(它比使用 BehaviorSubject 需要更多的代码和逻辑,所以我不推荐它,但它可能具有指导意义)

所以,这是一个使用 Observable instead of an EventEmitter 的实现.与我的 EventEmitter 实现不同,此实现还存储当前选择的 navItem在服务中,以便在创建观察组件时,它可以通过 API 调用检索当前值 navItem() , 然后通过 navChange$ 通知更改可观察。

import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/share';
import {Observer} from 'rxjs/Observer';

export class NavService {
  private _navItem = 0;
  navChange$: Observable<number>;
  private _observer: Observer;
  constructor() {
    this.navChange$ = new Observable(observer =>
      this._observer = observer).share();
    // share() allows multiple subscribers
  }
  changeNav(number) {
    this._navItem = number;
    this._observer.next(number);
  }
  navItem() {
    return this._navItem;
  }
}

@Component({
  selector: 'obs-comp',
  template: `obs component, item: {{item}}`
})
export class ObservingComponent {
  item: number;
  subscription: any;
  constructor(private _navService:NavService) {}
  ngOnInit() {
    this.item = this._navService.navItem();
    this.subscription = this._navService.navChange$.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:number;
  constructor(private _navService:NavService) {}
  selectedNavItem(item: number) {
    console.log('selected nav item ' + item);
    this._navService.changeNav(item);
  }
}

Plunker


另见 Component Interaction Cookbook example ,它使用 Subject除了可观察的。虽然示例是“父子通信”,但同样的技术也适用于不相关的组件。

关于angular - 委托(delegate) : EventEmitter or Observable in Angular,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34376854/

相关文章:

javascript - 如何打乱 JavaScript 数组?

angular - 如何在 typescript 中将base64 dataUrl转换为图像

angular - Ng Bootstrap 根据所选计数对星级颜色进行评级

iphone - 当观察者变为零时观察者会自动移除吗?

java - JTextField 的观察者模式?

android - RxScala/Java - 为什么我的 progressBar 没有显示?

Angular2 [innerHtml] angular2 标签不起作用

c++ - 如何使用 boost::signals 来实现观察者模式?

javascript - 可观察对象在 Redux 库源代码中是如何工作的?

angular - 错误 : "You provided ' undefined' where a stream was expected. 您可以在我的身份验证拦截器中提供 Observable、Promise、Array 或 Iterable。”