It seems using getters inside templates causes Angular change detection to go into a loop (getter gets called hundreds of times). After reading up a ton on similar issues out there I cannot seem to get a clear answer.
Background info:
I do believe using getters inside the template is the cleanest approach from a maintainability point of view, however seemingly because Angular cannot know if the getter value changed until it calls it, it just calls it all the time. I so far found three alternatives:
Option 1 would seem counterintuitive to the benefit of using Typescript classes. Option 2 would seem like unnecessary code duplication and reduce maintainability, option 3 would require a significant refactoring.
Here is an example (simplified for illustrative purpose)
Model:
export class UserModel {
private id: string;
get getId() {
console.log('Getting id');
return this.id;
}
set setId(id) {
this.id = id;
}
constructor() {
}
}
Component.html
<h1>Test</h1>
<p>{{user.getId}}</p>
Component.ts
import {Component, OnInit} from '@angular/core';
import {TestModel} from './test.model';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
public user: UserModel;
ngOnDestroy() {
if (this.userObserver) { this.userObserver.unsubscribe(); }
}
ngOnInit() {
this.userObserver = this.userObservable.subscribe(
(data: UserModel) => {
this.user = data;
},
(err: any) => {
console.error(err);
});
}
}
Will output the following console log: console.log output
Can anyone recommend the best practice to avoid unnecessary loops while working with complex models in Angular? Or even a proper way to debug this type of behavior, as it stands now I am console.logging the getter methods and watching for memmory usage spikes.
EDIT (Answer) After more time investigating, digging through stack traces I found out the infinite change detection loop was actually being caused by a service we inject called 'Sentry'. Apparently it causes an issue when console.log is used that triggers change detection. Found a github issue about it here: github.com/getsentry/sentry-javascript/issues/1883 Did not find a solution (seems inherently incompatible), will update if I find a fix for it.
Use ChangeDetectionStrategy.onPush
Run the following command to make this the default for your project when creating new components via the CLI.
ng config schematics.@schematics/angular.component.changeDetection OnPush
Generally speaking try to avoid complex getters or calling functions from within a template. If you need to transform data consider using Pipes, which are memoized.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With