web-dev-qa-db-ja.com

Angular 2イベントがダイジェストループ/検出サイクルをトリガーしないようにする方法は?

ドラッグアンドドロップ機能をAngular 2。

dragoverイベントを使用して、preventDefault()関数を実行しています。 dropイベントが この質問 で説明されているように機能するようにします。

dragoverメソッドは、コンポーネントのonDragOver関数によって処理されています。

<div draggable="true"
    (dragover)="onDragOver($event)">
...

コンポーネントでは、この関数はデフォルトの動作を防ぎ、ドラッグされたアイテムをこのターゲットにドロップできるようにします。

onDragOver(event) {
    event.preventDefault();
}

これは期待どおりに機能します。ドラッグオーバーイベントは、数百ミリ秒ごとに発生します。

しかし、onDragOver関数が呼び出されるたびに、Angular 2はそのダイジェストサイクルを実行します。これにより、アプリケーションの速度が低下します。ダイジェストサイクルをトリガーせずにこの関数を実行したいと思います。 。

これに使用する回避策は、要素イベントをサブスクライブし、次のようにAngular 2のコンテキスト外で実行することです。

constructor( ele: ElementRef, private ngZone: NgZone ) {
    this.ngZone.runOutsideAngular( () => {
        Observable.fromEvent(ele.nativeElement, "dragover")
            .subscribe( (event: Event) => {
                event.preventDefault();
            }
        );
    });
}

これは正常に動作します。しかし、nativeElementに直接アクセスすることなくこれを実現する方法はありますか?

15
nipuna777

1)1つの興味深い解決策は、EventManagerをオーバーライドすることです。

custom-event-manager.ts

import { Injectable, Inject, NgZone  } from '@angular/core';
import { EVENT_MANAGER_PLUGINS, EventManager } from '@angular/platform-browser';

@Injectable()
export class CustomEventManager extends EventManager {
  constructor(@Inject(EVENT_MANAGER_PLUGINS) plugins: any[], private zone: NgZone) {
    super(plugins, zone); 
  }

  addEventListener(element: HTMLElement, eventName: string, handler: Function): Function {
    if(eventName.endsWith('out-zone')) {
      eventName = eventName.split('.')[0];
      return this.zone.runOutsideAngular(() => 
          super.addEventListener(element, eventName, handler));
    } 

    return super.addEventListener(element, eventName, handler);
  }
}

app.module.ts

  ...
  providers: [
    { provide: EventManager, useClass: CustomEventManager }
  ]
})
export class AppModule {

使用法:

<h1 (click.out-zone)="test()">Click outside ng zone</h1>

<div (dragover.out-zone)="onDragOver($event)">

Plunkerの例

したがって、上記の解決策では、これらのオプションのいずれかを使用して、デフォルトの動作を防止し、angular zone:

(dragover.out-zone)="$event.preventDefault()"
(dragover.out-zone)="false"
(dragover.out-zone)="!!0"

2)Rob Wormaldが提供するもう1つのソリューションは Zonejsのブラックリストを使用

blacklist.ts

/// <reference types='zone.js/dist/zone.js' />

const BLACKLISTED_ZONE_EVENTS: string[] = [
  'addEventListener:mouseenter',
  'addEventListener:mouseleave',
  'addEventListener:mousemove',
  'addEventListener:mouseout',
  'addEventListener:mouseover',
  'addEventListener:mousewheel',
  'addEventListener:scroll',
  'requestAnimationFrame',
];

export const blacklistZone = Zone.current.fork({
  name: 'blacklist',
  onScheduleTask: (delegate: ZoneDelegate, current: Zone, target: Zone,
                   task: Task): Task => {

    // Blacklist scroll, mouse, and request animation frame events.
    if (task.type === 'eventTask' &&
        BLACKLISTED_ZONE_EVENTS.some(
            (name) => task.source.indexOf(name) > -1)) {
      task.cancelScheduleRequest();

      // Schedule task in root zone, note Zone.root != target,
      // "target" Zone is Angular. Scheduling a task within Zone.root will
      // prevent the infinite digest cycle from appearing.
      return Zone.root.scheduleTask(task);
    } else {
      return delegate.scheduleTask(target, task);
    }
  }
});

main.ts

import {blacklistZone} from './blacklist'

blacklistZone.run(() => {
  platformBrowser().bootstrapModuleFactory(...)
})

ブラックリスト付きのプランカー

更新:

5.0.0-beta.7(2017-09-13)

fix(platform-b​​rowser):ngZone外でBLACK_LISTED_EVENTSを実行

従う

更新2

Angular CLIには、macroTask/DomEventsパッチの一部を無効にするためのテンプレートが含まれています。

ちょうど開く

polyfills.ts

あなたはそこに次のコードを見つけることができます

/**
 * By default, zone.js will patch all possible macroTask and DomEvents
 * user can disable parts of macroTask/DomEvents patch by setting following flags
 */

 // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
 // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
 // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
 /*
 * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
 * with the following flag, it will bypass `zone.js` patch for IE/Edge
 */
// (window as any).__Zone_enable_cross_context_check = true;

https://github.com/angular/devkit/blob/8651a94380eccef0e77b509ee9d2fff4030fbfc2/packages/schematics/angular/application/files/sourcedir/ polyfills.ts#L55-L68

以下も参照してください。

21
yurzui

変更検出を切り離して、コンポーネントに対して変更検出が呼び出されないようにすることができます。

constructor(private cdRef:ChangeDetectorRef) {}
foo() {
  this.cdRef.detach();
  ...
  this.cdRef.attach();
}
9