我试图在Angular中实现类似委派模式的东西.

以下是场景:我有一个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!');
  }

}

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

推荐答案

Update 2016-06-27:而不是使用可观察的,使用

  • @Abdulrahman在 comments 中推荐的BehaviorSubject,或者
  • @Jason Goemaat在 comments 中推荐的ReplaySubject

Subject既是一个可观测值(因此我们可以对其使用subscribe())也是一个观察值(因此我们可以对其调用next()以发出新值).我们利用这个特性.一个主题允许将值多播给许多观察者.我们不利用这个特性(我们只有一个观察者).

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

下面的代码和plunker use BehaviorSubject.

ReplaySubject是科目的另一个变体.如果要等到实际产生一个值,请使用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


Original answer that uses an Observable:(与使用行为主题相比,它需要更多的代码和逻辑,所以我不推荐它,但它可能会很有启发性)

这是一个使用可观察instead of an EventEmitter的实现.与我的EventEmitter实现不同,此实现还将当前 Select 的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<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相关问答推荐

如何跨不同组件使用ng模板

Angular 17@在大小写时切换多个值

Angular 升级到v16和Ant设计错误:';nzComponentParams';类型';ModalOptions';中不存在

当可观察到的更改时,异步管道不更新视图

我的验证器收到一个始终为空的可观察值

如何处理后端删除元素后元素列表的刷新

Angular MSAL - 对 Bearer 令牌和 AUD 属性的怀疑

完成行为主体

在 kubernetes 上创建 Ingress 服务以将 springboot [后端] 连接到前端 [angular]

如何访问父组件中提供的 TemplateRef 内的服务?

在 angular2 中,如何获取在为 @Input 发送的对象上更改的属性的 onChanges

类继承支持

如何将服务变量传递到 Angular Material 对话框?

等待多个promises完成

NgrxStore 和 Angular - 大量使用异步管道或在构造函数中只订阅一次

如何添加/设置环境 Angular 6 angular.json 文件

Angular 2 单元测试 - @ViewChild 未定义

实现自定义 NgbDateParserFormatter 来更改 NgbInputDatepicker 上输入值的格式是否正确?

Angular2 Base64 清理不安全的 URL 值

Angular 2 组件不是任何 NgModule 的一部分