Angular 2如何防止事件触发摘要循环/检测周期? [英] Angular 2 how to keep event from triggering digest loop/detection cycle?

查看:78
本文介绍了Angular 2如何防止事件触发摘要循环/检测周期?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们正在使用Angular 2实现拖放功能.

We are implementing drag and drop functionality with Angular 2.

我正在使用dragover事件只是为了运行preventDefault()函数.因此drop事件的工作方式如此问题中所述.

I'm using the dragover event just to run the preventDefault() function. So that the drop event works as explained in this question.

dragover方法由组件中的onDragOver函数处理.

The dragover method is being handled by the onDragOver function in the component.

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

在组件中,此功能防止默认行为,允许将拖动的项目放置在此目标上.

In the component, this function prevents default behavior allowing for the dragged item to be dropped at this target.

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

这按预期工作.拖动事件每几百毫秒触发一次.

This works as expected. The dragover event gets fired every few hundred milliseconds.

但是,每次调用onDragOver函数时,Angular 2都会运行其摘要循环.这会使应用程序变慢.我想在不触发摘要周期的情况下运行此功能.

But, every time the onDragOver function is called, Angular 2 runs its digest cycle. This slows down the application. I'd like to run this function without triggering the digest cycle.

为此,一种解决方法是订阅元素事件,并在Angular 2的上下文之外运行它,如下所示:

A workaround we use for this is subscribing to element event and running it outside of the Angular 2's context as follows:

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

这很好.但是有没有一种方法可以实现而不必直接访问nativeElement呢?

This works fine. But is there a way to achieve this without having to access the nativeElement directly?

推荐答案

1)一种有趣的解决方案可能是覆盖EventManager

1) One interesting solution might be overriding 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 Example

因此,使用上述解决方案,您可以使用以下选项之一来防止默认行为并在角度区域之外运行事件:

So with solution above you can use one of these options to prevent default behavior and run event outside angular zone:

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

2) Rob Wormald提供的另一种解决方案是对Zonejs使用黑名单

2) One more solution provided by Rob Wormald is using blacklist for 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(...)
})

插入黑名单的

Plunker with blacklist

更新:

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

修复(平台浏览器):在ngZone外部运行BLACK_LISTED_EVENTS

关注

>://github.com/angular/angular/commit/e82812b9a9992bc275bba54575f9d780753c1 a>

https://github.com/angular/angular/commit/e82812b9a9992bc275bba54575f9d780753c1f8f

https://github.com/angular/angular/pull/21681

更新2

Angular cli包含用于禁用macroTask/DomEvents补丁程序某些部分的模板.

Angular cli includes template for disabling parts of macroTask/DomEvents patch.

只需打开

polyfills.ts

您可以在其中找到以下代码

You can find there the following code

/**
 * 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

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

另请参阅:

这篇关于Angular 2如何防止事件触发摘要循环/检测周期?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆