Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Huge performance impact on Component's host document click (Angular2)

So I have 300 instantiations of a component which defines global click event listener through a host property:

@Component({
  selector: 'value-edit',
  template: ``,
  host: {
    "(document: click)": "onClickOff($event)",
  },
  changeDetection: ChangeDetectionStrategy.OnPush
)
export class ValueEditComponent {
  onClickOff(globalEvent) {
    // to make sure it doesn't affect performance we keep it EMPTY!
  }
}

I noticed this hugely impacts performance, it takes about 2-3 seconds of processing after every click everywhere on a document.

This is JS CPU profile made in Chrome for a sequence: wait ~5 seconds, click, wait few seconds and stop recording. The click is the huge green column on the screenshot:

Single click on nothing reactive

I've tried detaching Change Detector on this component or even a parent but this didn't help. Simply commenting out the line "(document: click)": "onClickOff($event)", fixes the problem.

May be an issue of the framework or bad usage but I'm not sure how to qualify this or workaround in a more good-practice-way.

Plunker here

GitHub issue here

like image 308
Namek Avatar asked Oct 30 '22 19:10

Namek


1 Answers

Solution / workaround

On Angular 2.0.0 (final) code below will result in same performance issue:

ngAfterViewInit() {
    document.addEventListener('click', evt => this.evtClickHandler)
}

registering event outside the "zone" should help:

constructor(zone: NgZone) {
}

ngAfterViewInit() {
    this.zone.runOutsideAngular(() => {
        document.addEventListener('click', evt => this.offClickHandler(evt))
    })
}

Explanation

Performance issue happened again to me using Sortablejs library. It wasn't the case before final version of Angular but something changed due to registering events on native elements.

For sortablejs library I did this:

this.sortedImages = Sortable.create(el, options)

which now resulted in really bad performance while dragging elements:

enter image description here

Solution or workaround goes like this:

this.zone.runOutsideAngular(() => {
  this.sortedImages = Sortable.create(el, options)
})

where this.zone is injected @angular/core/NgZone. This way the library registers event listeners outside of NgZone.

I have posted an issue on GitHub about this problem but it was recognized as to be my error on coding, not bug in Angular. However, some changes appeared between latest (RC - before final) versions.

So this may be a bug or by (latest) design but I don't have confirmation on this.

like image 162
Namek Avatar answered Nov 15 '22 05:11

Namek