Angular ile Zone.js ve NgZone

Nahit Ferhat Ektaş
3 min readJun 17, 2023

Bir önceki yazımda, Angular component’larındaki değişime göre component’in yeniden render edilmesi sürecinin Change Detection Strategy ile yönetildiğinden bahsetmiştim.

Zone.js ise, asenkron olarak gerçekleştirdiğimiz işlemlerde Change Detection mekanizmasının tetiklenmesini sağlar. Change detection mekanisması da belirlediğimiz stratejiye göre(default veya onPush) componentimizi yeniden render eder. Biraz daha detaylandırmak gerekirse,

Örneğin; sayfamızda bir button’a tıkladık ve bir API’ye istek attık. İstek tamamlandığında zone.js devreye girer ve Change Detection mekanizmasına, sadece bir asenkron işlem gerçekleşti, belki bazı state’ler bundan etkilenmiş olabilir bilgisini verir. State’in değişip değişmediği veya bu asenkron işlemin hangi component da yer aldığı bilgisi zone.js’in sorumluğunda değildir. Yalnızca bir asenkron işlemin tamamlandığı bilgisini verir ve Change detection mekanizması da bu asenkron işlem neticesinde state’lerin etkilenebileceği varsayımı ile component’larımızı yeniden render eder.

@Component({
selector: 'app-root',
template: `
<div>
{{ counter }}
</div>
`,
})
export class AppComponent implements OnInit {
counter = 10;

ngOnInit(): void {
setInterval(() => {
this.counter = this.counter * 2;
}, 500);
}
}

Yukarıdaki kod örneğinde, counter adında bir değişkenimiz var ve bu değişken belirli aralıklar ile 2 ile çarpılarak yeniden render ediliyor. Yani aslında asenkron bir işlemimiz var ve bu bu asenkron işlem her gerçekleştiğinde zone.js change detection mekanizmasına haber veriyor.

Render edilen sayfa

Angular uygulama ilk ayağa kalkarken yani main.ts de yer alan bootstrapModule default olarak zone.js tanımlı olarak yüklü gelir. Ancak istenirse zone.js’i devre dışı bırakabilir.

platformBrowserDynamic()
.bootstrapModule(AppModule, { ngZone: 'noop' })
.catch((err) => console.error(err));

Uygulamanın ilk ayağa kalkacağı modul’e ekstra { ngZone:’noop’ } parametresini geçersek artık zone.js devre dışı kalacaktır. Ancak zone.js’in devre dışı kaldığı durumlarda sayfamızın render etme sürecini kendimiz yönetmemiz gerekecektir.

NgZone Service

NgZone service ile zone.js’i component’lerimize inject edebiliriz.

export class AppComponent implements OnInit {
counter = 10;

constructor(private ngZone: NgZone) {}

ngOnInit(): void {
setInterval(() => {
this.counter = this.counter * 2;
}, 500);
}
}

Inject ettiğimiz NgZone service ile, view tarafında render edilmesini istemediğimiz kod bloklarını çalıştırabilir ve böylelikle component’larımızın gereksiz yere yeniden render edilmesinin önüne geçebiliriz

NgZone service’sinde yer alan runOutsideAngular fonksiyonu, view’a etki etmesini istemediğimiz asenkron işlemleri Angular dan bağımsız olarak çalıştıracaktır.

export class AppComponent implements OnInit {
counter = 10;

constructor(private ngZone: NgZone) {}

ngOnInit(): void {
this.ngZone.runOutsideAngular(() => {
setInterval(() => {
this.counter = this.counter * 2;
}, 500);
});
}
}

Yukarıdaki örnekte setInterval(), runOutsideAngular() içerisinde çalıştığı için Angular’ın bu işlemden haberi olmayacak ve sayfamızda herhangi bir render işlemi gerçekleşmeyecektir.

Tekrar aynı işlemi Angular içerisinde kullanmak istiyorsak, yine NgZone service’sinde yer alan run() fonskiyonunu kullanmamız gerekir.

@Component({
selector: 'app-root',
template: `
<div>
{{ counter }}
</div>
<div>{{ message }}</div>
`,
})
export class AppComponent implements OnInit {
counter = 10;
message = '';

constructor(private ngZone: NgZone) {}

ngOnInit(): void {
this.ngZone.runOutsideAngular(() => {
setInterval(() => {
this.counter = this.counter - 1;
if (this.counter >= 5) {
this.ngZone.run(() => {
this.message = 'counter greater than 5';
});
} else if (this.counter <= 5 && this.counter >= 0) {
this.ngZone.run(() => {
this.message = 'counter less than 5';
});
}
}, 500);
});
}
}

Yukarıdaki kod örneğinde, counter değişkenine ek olarak message adında bir değişken daha tanımlıyoruz. Counter değişkeni setInterval() her çalıştığında 1 azalacaktır. Counter değişkeninin 0'ın altına düştüğü senaryolar, runOutsideAngular() ile Angular Zone dışında çalışacaktır ve yeniden render işlemine tabi olmayacaktır. 0 dan büyük olduğu durumlarda ise belirlediğimiz koşullara göre tekrar Angular Zone içerisinde run() fonksiyonu ile çağırılacaktır ve yeniden render işlemi gerçekleşecektir.

Render edilen sayfa

Yukarıdaki kod örneğine göre counter 0 olsa bile -1 alarak azalmaya devam ediyor, ama runOutsideAngular() ile işlem view da render edilmiyor. 0 dan büyük olduğu durumlardaki iş kurallarına göre run() ile render işlemi gerçekleşiyor.

--

--