15

I'm binding an scroll event to capture the scroll and do something with it, I've created a directive like bellow :

So I have simple directive which has nothing but :

      constructor ( private el : ElementRef ,
                        private renderer : Renderer ) {
              this.domAdapter = new browser.BrowserDomAdapter();
              this.ruler      = new Ruler( this.domAdapter );
      }
      ngAfterViewInit () : any {
              this.renderer.listenGlobal( 'window' , 'scroll' , ()=> {
                  console.log( 'scrolling' );
              } );
              return undefined;
      }

This is working fine , expect that I can see that it fires a change detection on scroll in all of my application.

This is inside one of my components :

     private  aFunction () {
             console.log( 'change detected !!!' );
     }

I have aFunction in a template somewhere in some component :

       <div>{{ aFunction() }}</div>

Previously, aFunction was getting fired off, only if I updated some input or clicked on a button , but now , it's getting that change detection on scroll!!! So my scrolling experience is laggy because of this !.

This is the normal behavior of Angular2 , all the events should fire change Detection , but I want to exclude my scroll event from this rule .

In a nutshell , how to define a event in Angular2 and turn of it's ability to fire change detection and make it manual.

I'm looking for :

    this.renderer.listenGlobalButDontFireTheChangeDetection
Milad
  • 27,506
  • 11
  • 76
  • 85

1 Answers1

13

I can offer you several hacks to do that:

1) Just set detection strategy to OnPush on your component:

@Component({
  ...
  changeDetection: ChangeDetectionStrategy.OnPush
})

The corresponding plunkr is here http://plnkr.co/edit/NPHQqEmldC1z2BHFCh7C?p=preview

2) Use zone.runOutsideAngular together with the native window.addEventListener:

this.zone.runOutsideAngular(() => {
  window.addEventListener('scroll', (e)=> {
    console.log( 'scrolling' );
  });
});

See also plunkr http://plnkr.co/edit/6Db1AIsTEGAirP1xM4Fy

3) Use zone.runOutsideAngular together with new instance of EventManager like this:

import { DomEventsPlugin, EventManager } from '@angular/platform-browser';
...
this.zone.runOutsideAngular(() => {
   const manager = new EventManager([new DomEventsPlugin()], new NgZone({enableLongStackTrace: false}));
   manager.addGlobalEventListener('window','scroll', (e) => {
     console.log( 'scrolling' ); 
   });
});

The plunkr is here http://plnkr.co/edit/jXBlM4fONKSNc7LtjChE?p=preview

I'm not sure that this is the right approach. Maybe it helps you in advancing... :)

Update:

Answer on this question: View is not updated on change in Angular2 gave me an idea for the third solution. Second solution is working because window was created outside of the angular zone. You can't do just:

this.zone.runOutsideAngular(() => {
  this.renderer.listenGlobal( 'window' , 'scroll' , ()=> {
      console.log( 'scrolling' );
  } ); 
});

It won't work because this.renderer was created inside angular zone. http://plnkr.co/edit/UKjPUxp5XUheooKuKofX?p=preview

I dont't know how to create a new instance Renderer(DomRenderer in our case) so i just created new instance EventManager outside of working zone and with new instance NgZone.

Community
  • 1
  • 1
yurzui
  • 205,937
  • 32
  • 433
  • 399
  • thanks mate , I can't use the first one , because I don't want to change all of my components change detection strategy, second and third one looks exactly like what I want , can you please elaborate more on them ? What's their difference? – Milad May 19 '16 at 09:37
  • This solved my problem , both second and third is working fine , what's the difference ? I wanted to use Renderer to make my code web worker friendly, which one of your answer is more like using Renderer and why ? Can you explain please ? – Milad May 19 '16 at 09:43
  • Yes, I'll try to write soon – yurzui May 19 '16 at 09:44
  • Does this work in Angular 2? I tried the 2nd method and my page still firing change detection when I scroll – João Silva Jul 10 '17 at 13:33
  • @JoãoSilva Please create plunker to reproduce – yurzui Jul 10 '17 at 13:34
  • I can't really reproduce since my data is loaded from a database, but I posted a question here: https://stackoverflow.com/questions/45011290/angular-2-performance-issue-rendering/45012198?noredirect=1#comment77000472_45012198 – João Silva Jul 10 '17 at 13:37
  • @JoãoSilva Just create mocked data that will be used in your minimal example – yurzui Jul 10 '17 at 13:39
  • 2
    Solution 1) does not work because angular will fire change detection on any dom event – albanx Nov 11 '18 at 15:19