如果通常导致更改检测运行的事件(setTimeout,setInterval,浏览器事件,ajax调用等)来自特定类(服务或组件),是否有办法完全禁用Angular的更改检测器?
也就是说,当我发现在我的服务中注册的setInterval
导致全局变化检测每秒运行时,我似乎完全错了。
我知道我可以将我的代码包装在NgZone.runOutsideAngular
方法的回调中,但是我更喜欢可以为整个类禁用更改检测器的解决方案,因为我在其中有其他代码块。不必要地运行检测的服务。
感谢您的帮助。
答案 0 :(得分:4)
一种可能的解决方案可能是您的服务的以下@RunOutsideAngular
装饰器:
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 强>
如果您想在某些课程中仅禁用setTimeout
和setInterval
,则可以修补这些功能
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']);
并像这样创建装饰器
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;
}
然后你可以按如下方式使用它
@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 强>
答案 1 :(得分:1)
更改检测从组件树的最顶部开始,由zone.js触发。
zone.js会捕获像setTimeout这样的异步操作,通知angular可能发生了更改。然后,Angular在组件树上自上而下运行更改检测。
从更改检测器中分离单个类将从更改检测中删除该类(即指令),但不会阻止更改检测在组件树的其余部分上运行。
根据您的需要, with(df1, (Q1==1) & (Q2 %in% 1:2)|(Q1 != 1 & is.na(Q2)))
是可行的方法,因为它会阻止zone.js通知有关您的异步操作的角度,从而完全阻止更改检测的运行。
答案 2 :(得分:1)
您可以使用以下内容更改各个组件的检测策略:
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'ws-layout-page',
templateUrl: './layout-page.component.html',
styleUrls: ['./layout-page.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class LayoutPageComponent {
}
我不知道任何其他方法可能会导致某些选择性开启/取决于来自哪里的信息