typescript - Angular: Tüm bir sınıf (servis veya bileşen) için Değişiklik Algılayıcısını devre dışı bırak
Normalde değişiklik tespitinin çalışmasına neden olan olaylar (setTimeout, setInterval, tarayıcı olayları, ajax çağrıları vb.) belirli bir sınıftan (servis veya bileşen) geliyorsa, Angular'ın değişiklik algılayıcısını tamamen devre dışı bırakmanın bir yolu var mı?
Yani, servisimde kaydedilen setInterval'in her saniye global değişiklik tespitini çalıştırdığını öğrendiğimde bana tamamen yanlış göründü..
Kodumu NgZone.runOutsideAngular metodunun geri çağrısının içine sarabileceğimin farkındayım, ama değişiklik algılayıcısının tüm bir sınıf için devre dışı bırakılabileceği çözümü tercih ederim, çünkü serviste algılamayı gereksiz yere çalıştıran başka kod parçalarım da var.
Yardımın için teşekkürler.
Cevap #1
Olası bir çözüm, servisinize yönelik aşağıdaki @RunOutsideAngular dekoratörü olabilir:
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 Örneği
Eğer bir sınıf içinde sadece setTimeout ve setInterval işlevlerini devre dışı bırakmak istiyorsanız, bu işlevleri yamalayabilirsiniz
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']);
ve bu şekilde bir dekoratör oluştur
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;
}
O zaman bunu şu şekilde kullanabilirsiniz
@RunOutsideAngular
export class Service {
constructor() {
setInterval(() => {
console.log('ctor tick');
}, 1000);
}
run() {
setTimeout(() => {
console.log('tick');
}, 1000);
setInterval(() => {
console.log('tick interval');
}, 1000)
}
}
Plunker Örneği
Cevap #2
Bireysel bileşenlerin algılama stratejisini aşağıdakilerle değiştirebilirsiniz:
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 {
}
Bilginin nereden geldiğine bağlı olarak seçici açma/kapama sağlayabilecek başka bir yöntem bilmiyorum.
