加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

angular – 从EventEmitter事件中最佳重新输入ngZone

发布时间:2020-12-17 17:47:32 所属栏目:安全 来源:网络整理
导读:有一个组件封装了一些库.为了避免所有这些库的事件监听器的变化检测噩梦,该库的范围在角区域之外: @Component({ ... })export class TestComponent { @Output() emitter = new EventEmittervoid(); constructor(private ngZone: NgZone) {} ngOnInit() { th
有一个组件封装了一些库.为了避免所有这些库的事件监听器的变化检测噩梦,该库的范围在角区域之外:

@Component({ ... })
export class TestComponent {

  @Output()
  emitter = new EventEmitter<void>();

  constructor(private ngZone: NgZone) {}

  ngOnInit() {
    this.ngZone.runOutsideAngular(() => {
        // ...
    });    
  }

}

这一切都非常清楚和普遍.现在让我们添加事件来发出动作:

@Component({ ... })
export class TestComponent {

  @Output()
  emitter = new EventEmitter<void>();

  private lib: Lib;

  constructor(private ngZone: NgZone) {}

  ngOnInit() {
    this.ngZone.runOutsideAngular(() => {
      this.lib = new Lib();
    });

    this.lib.on('click',() => {
      this.emitter.emit();
    });
  }

}

问题是这个发射器不会触发变化检测,因为它是在区域外触发的.那么有可能重新进入该区域:

@Component({ ... })
export class TestComponent {

  @Output()
  emitter = new EventEmitter<void>();

  private lib: Lib;

  constructor(private ngZone: NgZone) {}

  ngOnInit() {
    this.ngZone.runOutsideAngular(() => {
      this.lib = new Lib();
    });

    this.lib.on('click',() => {
      this.ngZone.run(() => this.emitter.emit());
    });
  }

}

最后,我提出了这个问题.即使我没有在父组件中监听此事件,this this.ngZone.run也会强制进行更改检测:

<test-component></test-component>

这是不想要的,因为,我没有订阅那个事件=>没有什么可以检测的.

什么可以解决这个问题?

对于那些对现实生活中的例子感兴趣的人,问题的起源是here.

解决方法

首先,感谢cgTag的回答.它引导我进入更好的方向,更易读,使用舒适,而不是getter使用Observable自然懒惰.

这是一个解释清楚的例子:

export class Component {

  private lib: any;

  @Output() event1 = this.createLazyEvent('event1');

  @Output() event2 = this.createLazyEvent<{ eventData: string; }>('event2');

  constructor(private el: ElementRef,private ngZone: NgZone) { }

  // creates an event emitter that binds to the library event
  // only when somebody explicitly calls for it: `<my-component (event1)="..."></my-component>`
  private createLazyEvent<T>(eventName: string): EventEmitter<T> {
    // return an Observable that is treated like EventEmitter
    // because EventEmitter extends Subject,Subject extends Observable
    return new Observable(observer => {
      // this is mostly required because Angular subscribes to the emitter earlier than most of the lifecycle hooks
      // so the chance library is not created yet is quite high
      this.ensureLibraryIsCreated();

      // here we bind to the event. Observables are lazy by their nature,and we fully use it here
      // in fact,the event is getting bound only when Observable will be subscribed by Angular
      // and it will be subscribed only when gets called by the ()-binding
      this.lib.on(eventName,(data: T) => this.ngZone.run(() => observer.next(data)));

      // important what we return here
      // it is quite useful to unsubscribe from particular events right here
      // so,when Angular will destroy the component,it will also unsubscribe from this Observable
      // and this line will get called
      return () => this.lib.off(eventName);
    }) as EventEmitter<T>;
  }

  private ensureLibraryIsCreated() {
    if (!this.lib) {
      this.ngZone.runOutsideAngular(() => this.lib = new MyLib());
    }
  }

}

这是另一个示例,其中使用了库实例observable(每次重新创建时都会发出库实例,这是一种非常常见的情况):

private createLazyEvent<T>(eventName: string): EventEmitter<T> {
    return this.chartInit.pipe(
      switchMap((chart: ECharts) => new Observable(observer => {
        chart.on(eventName,(data: T) => this.ngZone.run(() => observer.next(data)));
        return null; // no need to react on unsubscribe as long as the `dispose()` is called in ngOnDestroy
      }))
    ) as EventEmitter<T>;
  }

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读