我们正在使用Angular 2实现拖放功能。
我正在使用dragover
事件来运行preventDefault()
函数。因此,drop
事件的工作方式与this question中所述相同。
dragover
方法由组件中的onDragOver
函数处理。
<div draggable="true"
(dragover)="onDragOver($event)">
...
在组件中,此功能可防止默认行为,允许将拖动的项目放在此目标上。
onDragOver(event) {
event.preventDefault();
}
这可以按预期工作。每隔几百毫秒就会触发一次dragover事件。
但是,每次调用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?
答案 0 :(得分:19)
1)一个有趣的解决方案可能是覆盖EventManager
自定义事件-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 强>
因此,使用上述解决方案,您可以使用以下选项之一来防止默认行为并在角度区域外运行事件:
(dragover.out-zone)="$event.preventDefault()"
(dragover.out-zone)="false"
(dragover.out-zone)="!!0"
2) Rob Wormald提供的另一个解决方案是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)
fix(platform-browser): run BLACK_LISTED_EVENTS outside of ngZone
关注
更新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;
另见:
答案 1 :(得分:9)
您可以分离更改检测器以防止为组件调用更改检测
constructor(private cdRef:ChangeDetectorRef) {}
foo() {
this.cdRef.detach();
...
this.cdRef.attach();
}