当前位置: 首页 > 知识库问答 >
问题:

委派:事件发射器或角观察

孟承嗣
2023-03-14

我试图在Angular中实现类似委托模式的东西。当用户单击nav-project时,我想调用一个函数,然后该函数发出一个事件,该事件反过来应该由监听该事件的其他组件处理。

下面是一个场景:我有一个导航组件:

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

}

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

共有3个答案

马边浩
2023-03-14

您可以使用:

  1. 行为主题:

行为主体是主体的一种类型,主体是一种特殊类型的可观察对象,可作为可观察者和观察者。您可以像任何其他可观察者一样订阅消息,订阅后,它返回源可观察对象发出的主体的最后一个值:

优点:在组件之间传递数据不需要像父子关系这样的关系。

导航服务

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

@Injectable()
export class NavService {
  private navSubject$ = new BehaviorSubject<number>(0);

  constructor() {  }

  // Event New Item Clicked
  navItemClicked(navItem: number) {
    this.navSubject$.next(number);
  }

 // Allowing Observer component to subscribe emitted data only
  getNavItemClicked$() {
   return this.navSubject$.asObservable();
  }
}

导航组件

@Component({
  selector: 'navbar-list',
  template:`
    <ul>
      <li><a (click)="navItemClicked(1)">Item-1 Clicked</a></li>
      <li><a (click)="navItemClicked(2)">Item-2 Clicked</a></li>
      <li><a (click)="navItemClicked(3)">Item-3 Clicked</a></li>
      <li><a (click)="navItemClicked(4)">Item-4 Clicked</a></li>
    </ul>
})
export class Navigation {
  constructor(private navService:NavService) {}
  navItemClicked(item: number) {
    this.navService.navItemClicked(item);
  }
}

观察组件

@Component({
  selector: 'obs-comp',
  template: `obs component, item: {{item}}`
})
export class ObservingComponent {
  item: number;
  itemClickedSubcription:any

  constructor(private navService:NavService) {}
  ngOnInit() {

    this.itemClickedSubcription = this.navService
                                      .getNavItemClicked$
                                      .subscribe(
                                        item => this.selectedNavItem(item)
                                       );
  }
  selectedNavItem(item: number) {
    this.item = item;
  }

  ngOnDestroy() {
    this.itemClickedSubcription.unsubscribe();
  }
}

第二种方法是向上的事件委托-

e、 g由@Ashish Sharma回答。

江睿
2023-03-14

突发新闻:我添加了另一个答案,它使用了可观察的而不是事件发射器。我推荐那个答案而不是这个。实际上,在服务中使用EventEmitter是一种糟糕的做法。

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

将EventEmitter放入服务中,该服务允许ObservingComponent直接订阅(和取消订阅)事件

import {EventEmitter} from 'angular2/core';

export class NavService {
  navchange: EventEmitter<number> = 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:`
    <div class="nav-item" (click)="selectedNavItem(1)">item 1 (click me)</div>
  `,
})
export class Navigation {
  constructor(private navService:NavService) {}
  selectedNavItem(item: number) {
    console.log('selected nav item ' + item);
    this.navService.emit(item);
  }
}

如果您尝试使用Plunker,我不喜欢这种方法:

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

更新:解决第二个问题的另一个选择是让观察组件直接订阅navChangeEventEmitter属性:

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));
}
丌官开宇
2023-03-14

更新2016-06-27:不使用可见光,而是使用

  • @Abdulrahman在评论中推荐的行为主体,或
  • 由@Jason Goemaat在评论中推荐的ReplaySubject

一个主题既是一个可观察的(所以我们可以订阅它),也是一个观察者(所以我们可以在它上面调用下一个()来发出一个新值)。我们利用这个特性。主题允许将值多播到多个观察者。我们不利用此功能(我们只有一个观察者)。

行为主体是主体的变体。它有“当前值”的概念。我们利用了这一点:每当我们创建一个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需要更多的代码和逻辑,所以我不推荐它,但它可能很有启发性)

因此,这里有一个实现,它使用一个可观察的而不是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

另请参见组件交互烹饪书示例,该示例除了使用可观察对象外,还使用了主题。尽管示例是“家长和孩子沟通”,但相同的技术也适用于不相关的组件。

 类似资料:
  • 问题内容: 因此,如果我有一个带有100 li的ul,则每个li都应该有ng- clicks或是否有办法将事件绑定到ul并将其委派给li的jquery的工作方式?这会好还是坏?我们有100个事件,还是最后只有一个事件? 问题答案: 似乎angular并没有使用中继器进行事件委派。有人在github上发布了一个关于它的问题。争论在于它是否实际上会带来更好的性能。 可能有解决方法,但需要jQuery。

  • 将事件侦听器添加到可以使用事件委派的元素。 使用 EventTarget.addEventListener() 将一个事件监听器添加到一个元素。 如果提供了 选项对象(opts) 的 target 属性,确保事件目标匹配指定的目标元素,然后通过提供正确的 this 上下文来调用回调。 返回一个对自定义委派函数的引用,以便与 off 一起使用。 忽略 opts ,则默认为非委派行为,并且事件冒泡。

  • 我正在按照NestJS文档创建事件发射器(文档链接)。当我运行代码时,我得到一个错误: [Nest]129586-16/06/2021,20:43:31[ExceptionHandler]this.eventEmitter.emit不是函数 这就是我的代码的样子: 我不确定我错过了什么。

  • Node中的许多对象都会发出事件,例如net.Server每次对等体连接它时都会发出一个事件,fs.readStream会在打开文件时发出事件。 发出事件的所有对象都是events.EventEmitter的实例。 EventEmitter类 正如我们在上一节中看到的,EventEmitter类位于events模块中。 可通过以下代码访问 - // Import events module var

  • 捕获和冒泡允许我们实现一种被称为 事件委托 的强大的事件处理模式。 这个想法是,如果我们有许多以类似方式处理的元素,那么就不必为每个元素分配一个处理程序 —— 而是将单个处理程序放在它们的共同祖先上。 在处理程序中,我们获取 event.target 以查看事件实际发生的位置并进行处理。 让我们看一个示例 —— 反映中国古代哲学的 八卦图。 如下所示:在新窗口中打开 在沙箱中打开 其 HTML 如

  • 我在angular指令中有这段代码,我发现$watch的行为有点令人困惑。在“ng click”中调用updateSelect: 当我点击按钮(触发更新选择)并观看控制台时,我会看到“更新”,然后是“观看”。函数内部发生的第一件事是选择Ctrl。设置了activeList,所以我希望看到“watch”和“update”。 不应该在阵列更改后立即监视触发器吗?