Angular 2 drag and drop directive is very slow

I am trying to implement a custom drag and drop directive. It works, but it is very slow, and I think that slowness can be traced back to Angular 2, because I have never encountered this slowness before. Slowing occurs only when I attach an event listener to dragover or drag events (i.e. Events that are sent often), even if I do nothing, but return false in them.

Here is my directive code:

 import {Directive, ElementRef, Inject, Injectable} from 'angular2/core'; declare var jQuery: any; declare var document: any; @Directive({ selector: '.my-log', host: { '(dragstart)': 'onDragStart($event)', '(dragover)': 'onDragOver($event)', '(dragleave)': 'onDragLeave($event)', '(dragenter)': 'onDragEnter($event)', '(drop)': 'onDrop($event)', } }) @Injectable() export class DraggableDirective { refcount = 0; jel; constructor( @Inject(ElementRef) private el: ElementRef) { el.nativeElement.setAttribute('draggable', 'true'); this.jel = jQuery(el.nativeElement); } onDragStart(ev) { ev.dataTransfer.setData('Text', ev.target.id); } onDragOver(ev) { return false; } onDragEnter(ev) { if (this.refcount === 0) { this.jel.addClass('my-dragging-over'); } this.refcount++; } onDragLeave(ev) { this.refcount--; if (this.refcount === 0) { this.jel.removeClass('my-dragging-over'); } } onDrop(ev) { this.jel.removeClass('my-dragging-over'); this.refcount = 0; } } 

Here you will find the corresponding passage of styles:

 .my-log.my-dragging-over { background-color: yellow; } 

As you can see, all I do is select an element that is being dragged in yellow. And it works fast when I do not handle the dragover event, however I have to handle it to support deletion. When I handle the dragover event, everything slows down to unbearable levels!

EDIT I am using Angular beta 2.0.0-beta.8

EDIT # 2 . I tried profiling the code using the chrome profiler, these are the results:

Imgur

Look at the marked line, this is strangely suspicious ...

EDIT # 3 A problem has been discovered: it is really related to detecting changes in Angular 2. The drag and drop operation in my case is performed on a very dense page with a lot of bindings and directives. When I commented on everything except this list, it worked fast again ... Now I need your help finding a solution!

EDIT # 4 SOLVED

The problem was detecting changes, but the error was not with Angular code, but with my own ineffective bindings. I had many bindings of this type:

 *ngFor="#a of someFunc()" 

This led to the fact that Angular did not know whether the data changed or not, and the someFunc function was called again and again, even though the data did not change during the drag and drop process. I modified these bindings to refer to simple properties in my class, and moved the code that populates them where it should have been. Everything started again with lightning speed!

Thanks!

+7
source share
6 answers

Answering my own question (the problem has been resolved).

The slowness problem was caused by inefficient data bindings in my markup, which led Angular to spend a lot of time calling functions on my view model. I had many bindings of this type:

 *ngFor="#a of someFunc()" 

This led to the fact that Angular did not know whether the data changed or not, and the someFunc function was called again and again after each start of onDragOver (which is about once every 350 ms), although the data was not changed during the drag and drop process. I modified these bindings to refer to simple properties in my class, and moved the code that populates them where it should have been. Everything started again with lightning speed!

LLAP!

+4
source

Just the problem was with the same problem. Even with efficient ngFor code ngFor dragging and dropping can be crazy if you have a large number of draggable items.

The trick for me was to have all drag and drop event listeners run outside of Angular using ngZone , then ngZone it back into Angular when uninstalled. This allows Angular to avoid detection checks for each pixel that the item being moved around.

Inject:

 import { Directive, ElementRef, NgZone } from '@angular/core'; constructor(private el: ElementRef, private ngZone: NgZone) {} 

Initializing:

 ngOnInit() { this.ngZone.runOutsideAngular(() => { el.addEventListener('dragenter', (e) => { // do stuff with e or el }); ... 

Knockback:

 el.addEventListener('drop', (e) => { this.ngZone.run(() => { console.log("dropped"); }) }) 
+7
source

Thanks to everyone for this discussion. As a result, we get a simple solution that works like a charm:

 constructor(private cd: ChangeDetectorRef) { } drag(event: DragEvent): void { this.cd.detach(); // Begin the job (use event.dataTransfer) } allowDrop(event: DragEvent): void { event.preventDefault(); } drop(event: DragEvent): void { event.preventDefault(); this.cd.reattach(); // Do the job } 
+2
source

I had a similar problem, also my drag and drop became very slow when I made several drag zones inside *ngFor .

I solved this by changing the change detection strategy to the OnPush child component.

Then each time the item is dragged, do markForCheck() .

 constructor(private changeDetectorRef: ChangeDetectorRef) {} // Callback function public onDrag() { this.changeDetectorRef.markForCheck(); } 
0
source

For me, the problem was that the development mode was turned on even in production. When I compiled it with ng build --evn-prod suddenly became fast.

0
source

I recently had a similar problem. It was in a corner environment using reactive forms. Here's how I solved it for my situation:

In short, I turned off change detection for this component during drag and drop.

  1. import ChangeDetectorRef:
  import { ChangeDetectorRef } from '@angular/core'; 
  1. enter it in the constructor:
  constructor(private chngDetRef: ChangeDetectorRef) { //... 
  1. disable it on dragStart:
  private onDragStart(event, dragSource, dragIndex) { // ... this.chngDetRef.detach(); // ... 
  1. plug it in drop by drop and drag:
  private onDrop(event, dragSource, dragIndex) { // ... this.chngDetRef.reattach(); // ... private onDragEnd(event, dragIndex) { // ... this.chngDetRef.reattach(); // ... 

If you have many parent or layered components, you may have to do something to detect their changes in order to see a significant improvement.

Good luck

0
source

All Articles