Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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
like image 298
Milad Avatar asked May 19 '16 06:05

Milad


People also ask

How often does scroll event fire?

The scroll event fires when the document view has been scrolled. For element scrolling, see Element: scroll event . Note: In iOS UIWebViews, scroll events are not fired while scrolling is taking place; they are only fired after the scrolling has completed. See Bootstrap issue #16202.

What are scroll start and scroll stop events?

For a page, the scrollX and scrollY properties return the number of pixels that the document is currently scrolled horizontally and vertically. For an element, the scrollTop and scrollLeft properties set or get the number of pixels that the element's content is vertically scrolled and scrolled from its left edge.

How does Onscroll work?

The onscroll event occurs when an element's scrollbar is being scrolled. Tip: use the CSS overflow style property to create a scrollbar for an element.


1 Answers

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.

like image 61
yurzui Avatar answered Sep 20 '22 01:09

yurzui