Angular: Disable Change Detector for an entire class (service or component)

后端 未结 3 515
终归单人心
终归单人心 2021-01-24 20:07

Is there a way to completely disable Angular\'s change detector if events that normally cause the change detection to run (setTimeout, setInterval, browser events, ajax calls et

3条回答
  •  梦谈多话
    2021-01-24 20:50

    One possible solution might be the following @RunOutsideAngular decorator for your service:

    declare let Zone: any;
    
    export function RunOutsideAngular(target: any) {
      Object.getOwnPropertyNames(target.prototype)
        .filter(p => typeof target.prototype[p] === 'function')
        .forEach(p => {
          let originalMethod = target.prototype[p];  
          target.prototype[p] = function (...args) {
            let self = this;
            Zone.root.run(() => originalMethod.apply(self, args));
          }
        });
    
      let ctor: any = function (...args) {
        let self = this;
        return Zone.root.run(() => target.apply(self, args));
      };
      ctor.prototype = target.prototype;
      return ctor;
    }
    

    Plunker Example

    If you want to disable only setTimeout and setInterval within some class you can patch these functions

    function patchTimers(timers: any[]) {
        timers.forEach((timer) => {
            let originalMethod = window[timer];
            window[timer] = function (...args) {
                let self = this;
                if (Zone.current['__runOutsideAngular__'] === true && Zone.current.name === 'angular') {
                    Zone.root.run(() => originalMethod.apply(self, args));
                } else {
                    originalMethod.apply(this, arguments);
                }
            };
        })
    }
    patchTimers(['setTimeout', 'setInterval']);
    

    and create decorator like this

    export function RunOutsideAngular(target: any) {
        Object.getOwnPropertyNames(target.prototype)
            .filter(p => typeof target.prototype[p] === 'function')
            .forEach(p => {
                let originalMethod = target.prototype[p];
                target.prototype[p] = function (...args) {
                    Zone.current['__runOutsideAngular__'] = true;
                    originalMethod.apply(this, args);
                    delete Zone.current['__runOutsideAngular__'];
                }
            });
    
        let ctor: any = function (...args) {
            Zone.current['__runOutsideAngular__'] = true;
            let instance = target.apply(this, args);
            delete Zone.current['__runOutsideAngular__'];
            return instance;
        };
        ctor.prototype = target.prototype;
        return ctor;
    }
    

    Then you can use it as follows

    @RunOutsideAngular
    export class Service {
      constructor() {
        setInterval(() => {
          console.log('ctor tick');
        }, 1000);
      }
    
      run() {
        setTimeout(() => {
          console.log('tick');
        }, 1000);
    
        setInterval(() => {
          console.log('tick interval');
        }, 1000)
      }
    }
    

    Plunker Example

提交回复
热议问题