委托:EventEmitter或可观察到的Angular [英] Delegation: EventEmitter or Observable in Angular

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

问题描述

我正在尝试在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 ?

推荐答案

更新2016-06-27:而不是使用Observables,要么使用

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

    由@Abdulrahman在评论中推荐的
  • BehaviorSubject,或者
  • 一个ReplaySubject,由@Jason Goemaat在评论中建议
  • a BehaviorSubject, as recommended by @Abdulrahman in a comment, or
  • a ReplaySubject, as recommended by @Jason Goemaat in a comment

主题都是可观察的(因此我们可以subscribe()它)和一个观察者(因此我们可以在其上调用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.

下面的代码和朋克使用BehaviorSubject.

The code below and the plunker use BehaviorSubject.

ReplaySubject 是Subject的另一个变体.如果要等到实际生成一个值,请使用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);
  }
}

柱塞

使用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$可观察.

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

柱塞

另请参见组件交互食谱示例,除了可观察对象外,还使用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.

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

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