委托:Angular 中的 EventEmitter 或 Observable [英] Delegation: EventEmitter or Observable in Angular

查看:20
本文介绍了委托:Angular 中的 EventEmitter 或 Observable的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to implement something like a delegation pattern in Angular. When the user clicks on a nav-item, I would like to call a function which then emits an event which should in turn be handled by some other component listening for the event.

这里是场景:我有一个 Navigation 组件:

Here is the scenario: I have a Navigation component:

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!');
  }

}

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

The key question is, how do I make the observing component observe the event in question ?

推荐答案

Update 2016-06-27: 不使用 Observables,而是使用任一

Update 2016-06-27: instead of using Observables, use either

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

A Subject 都是 Observable(所以我们可以订阅() 到它)和一个观察者(所以我们可以在它上面调用 next() 来发出一个新值).我们利用了这个特性.主题允许将值多播到许多观察者.我们不利用此功能(我们只有一个观察者).

A Subject is both an Observable (so we can subscribe() to it) and an Observer (so we can call next() on it to emit a new value). We exploit this feature. A Subject allows values to be multicast to many Observers. We don't exploit this feature (we only have one Observer).

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

BehaviorSubject is a variant of Subject. It has the notion of "the current value". We exploit this: whenever we create an ObservingComponent, it gets the current navigation item value from the BehaviorSubject automatically.

下面的代码和 plunker 使用 BehaviorSubject.

The code below and the plunker use BehaviorSubject.

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

ReplaySubject is another variant of Subject. If you want to wait until a value is actually produced, use ReplaySubject(1). Whereas a BehaviorSubject requires an initial value (which will be provided immediately), ReplaySubject does not. ReplaySubject will always provide the most recent value, but since it does not have a required initial value, the service can do some async operation before returning it's first value. It will still fire immediately on subsequent calls with the most recent value. If you just want one value, use first() on the subscription. You do not have to unsubscribe if you use 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 更多的代码和逻辑,所以我不推荐它,但它可能有指导意义)

Original answer that uses an Observable: (it requires more code and logic than using a BehaviorSubject, so I don't recommend it, but it may be instructive)

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

So, here's an implementation that uses an Observable instead of an EventEmitter. Unlike my EventEmitter implementation, this implementation also stores the currently selected navItem in the service, so that when an observing component is created, it can retrieve the current value via API call navItem(), and then be notified of changes via the navChange$ Observable.

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

另请参阅组件交互手册示例,除了 observables 之外,它还使用 Subject.尽管示例是父子通信",但同样的技术也适用于不相关的组件.

See also the Component Interaction Cookbook example, which uses a Subject in addition to observables. Although the example is "parent and children communication," the same technique is applicable for unrelated components.

这篇关于委托:Angular 中的 EventEmitter 或 Observable的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆