ドラッグアンドドロップ機能を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に直接アクセスすることなくこれを実現する方法はありますか?
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)">
したがって、上記の解決策では、これらのオプションのいずれかを使用して、デフォルトの動作を防止し、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-browser):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;
以下も参照してください。
変更検出を切り離して、コンポーネントに対して変更検出が呼び出されないようにすることができます。
constructor(private cdRef:ChangeDetectorRef) {}
foo() {
this.cdRef.detach();
...
this.cdRef.attach();
}