Prevent a native browser event ( like scroll ) from firing change detection
Asked Answered
M

1

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
Melioration answered 19/5, 2016 at 6:47 Comment(0)
C
14

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.

Cloudland answered 19/5, 2016 at 9:5 Comment(8)
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?Melioration
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 ?Melioration
Yes, I'll try to write soonCloudland
Does this work in Angular 2? I tried the 2nd method and my page still firing change detection when I scrollCovetous
@JoãoSilva Please create plunker to reproduceCloudland
I can't really reproduce since my data is loaded from a database, but I posted a question here: #45011790Covetous
@JoãoSilva Just create mocked data that will be used in your minimal exampleCloudland
Solution 1) does not work because angular will fire change detection on any dom eventEssam

© 2022 - 2024 — McMap. All rights reserved.