委托:在Angular中使用EventEmitter或Observable。

22 浏览
0 Comments

委托:在Angular中使用EventEmitter或Observable。

我正在尝试在 Angular 中实现类似委托模式的东西。当用户点击 nav-item 时,我想调用一个函数来发出一个事件,然后再由另一个组件监听事件来处理它。

以下是方案:我有一个 Navigation 组件:

import {Component, Output, EventEmitter} from 'angular2/core';
@Component({
    // other properties left out for brevity
    events : ['navchange'], 
    template:`
    `
})
export class Navigation {
    @Output() navchange: EventEmitter = 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!');
  }
}

关键问题是,如何使观察组件观察到这个事件?

admin 更改状态以发布 2023年5月24日
0
0 Comments

突发新闻:我添加了另一个答案,该答案使用Observable而不是EventEmitter。我建议使用那个答案而不是这个。而实际上,在服务中使用EventEmitter是不良实践


原始答案:(不要这样做)

将EventEmitter放入一个服务中,这样ObservingComponent可以直接订阅(和取消订阅)事件

import {EventEmitter} from 'angular2/core';
export class NavService {
  navchange: EventEmitter = new EventEmitter();
  constructor() {}
  emit(number) {
    this.navchange.emit(number);
  }
  subscribe(component, callback) {
    // set 'this' to component when callback is called
    return this.navchange.subscribe(data => call.callback(component, data));
  }
}
@Component({
  selector: 'obs-comp',
  template: 'obs component, index: {{index}}'
})
export class ObservingComponent {
  item: number;
  subscription: any;
  constructor(private navService:NavService) {
   this.subscription = this.navService.subscribe(this, this.selectedNavItem);
  }
  selectedNavItem(item: number) {
    console.log('item index changed!', item);
    this.item = item;
  }
  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}
@Component({
  selector: 'my-nav',
  template:`
    item 1 (click me)
  `,
})
export class Navigation {
  constructor(private navService:NavService) {}
  selectedNavItem(item: number) {
    console.log('selected nav item ' + item);
    this.navService.emit(item);
  }
}

如果您尝试Plunker,我不喜欢这种方法的几个方面:

  • ObservingComponent在销毁时需要取消订阅
  • 我们必须将组件传递给subscribe(),以便在调用回调时设置正确的this

更新:解决第二个问题的另一种替代方法是让ObservingComponent直接订阅navchange EventEmitter属性:

constructor(private navService:NavService) {
   this.subscription = this.navService.navchange.subscribe(data =>
     this.selectedNavItem(data));
}

如果我们直接订阅,那么就不需要在NavService上使用subscribe()方法了。

为了使NavService稍微更加封装,您可以添加一个getNavChangeEmitter()方法并使用它:

getNavChangeEmitter() { return this.navchange; }  // in NavService
constructor(private navService:NavService) {  // in ObservingComponent
   this.subscription = this.navService.getNavChangeEmitter().subscribe(data =>
     this.selectedNavItem(data));
}

0
0 Comments

更新 2016-06-27:不要使用Observables,可以使用以下任何一种方法中的一种:

  • BehaviorSubject,由 @Abdulrahman 推荐,或
  • ReplaySubject,由 @Jason Goemaat 推荐

A Subject 既是 Observable(所以我们可以对其进行订阅),也是 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(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:`
    nav 1 (click me)
    nav 2 (click me)`
})
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 实现而不是 EventEmitter的版本。与我的 EventEmitter 实现不同,这个实现还将当前选择的 navItem 存储在服务中,因此当创建一个观察组件时,它可以通过 API 调用 navItem() 检索当前值,然后通过 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;
  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:`
    nav 1 (click me)
    nav 2 (click me)
  `,
})
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。虽然示例是“父子通信”,但是同样的技术也适用于不相关的组件。

0