Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
616 views
in Technique[技术] by (71.8m points)

angular - 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
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...