Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular change detection loop triggered by using getters inside template

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:

  1. Stop using getters, make all properties public and access them directly
  2. Store the value of each getter into a public property on the component and bind that instead of the getter in the template
  3. Change the changeDetection mode of Angular from default to onPush

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.

like image 749
d_stack Avatar asked Mar 22 '19 13:03

d_stack


1 Answers

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.

like image 73
glued Avatar answered Nov 13 '22 09:11

glued