Sunday, June 29, 2025

changeDetectorRef.markForCheck()

When using changeDetection: ChangeDetectionStrategy.OnPush, you need to call changeDetectorRef.markForCheck(). To avoid explicitly calling markForCheck, use async pipe on the observable variable. See line 13 on HTML below
@Component({
  selector: 'app-contact-us',
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [JsonPipe, CommonModule, FormsModule, JsonPipe],
  templateUrl: './contact-us.html',
  styles: ``,
})

export class ContactUs implements OnInit {
  weather: any = null;
  weatherToo: any = null;
  weather$: any = null;
  weatherToo$: any = null;
  
  // not working
  // @Inject(ChangeDetectorRef) private readonly cdr!: ChangeDetectorRef;

  // working, but the obvious benefits are still not clear
  // private readonly cdr = inject(ChangeDetectorRef);

  constructor(private http: HttpClient, private cdr: ChangeDetectorRef) {}

  ngOnInit(): void  {
    this.http.get('/api/WeatherForecast').subscribe({
      next: value => {
        // console.log({value});
        this.weather = value;
        this.cdr.markForCheck(); // will reflect on the UI

        // use this when you need it be immediate, like in setTimeout or 3rd party library
        // this.cdr.detectChanges();
      },
      error: (err) => console.error('http error', err)
    });

    firstValueFrom(this.http.get('/api/WeatherForecast')).then(value => {
      this.weatherToo = value;
      this.cdr.markForCheck();
    });

    this.weather$ = this.http.get('/api/WeatherForecast');
    // this.weather$.subscribe({
    //   next: (value: any) => {
    //     console.log('see', {value});
    //   },
    //   error: (err: any) => console.error('http error', err)
    // });

    this.weatherToo$ = this.http.get('/api/WeatherForecast');
  }
}
<div>
  <hr/>
  This works:<br/>
  {{weather | json}}
  <hr/>
  This too:<br/>
  {{weatherToo | json}}
  <hr/>
  This too, and it implicitly calls changeDetectorRef.markForCheck():<br/>
  {{weather$ | async | json}}

  <hr/>
  This will not show null:<br/>
  <ng-container *ngIf="weatherToo$ | async as weatherWhenHaveValue">{{ weatherWhenHaveValue | json }}</ng-container>

  <hr/>
  <input [(ngModel)]="name" placeholder="Name"/>
</div>
Ask this on Gemini or ChatGPT and see the answer:
does this recommendation kinda defeat the purpose of optimization for when change detection should take effect?

Option 3: Use AsyncPipe in the template
This forces Angular to update on observable emissions, even in OnPush:

weather$ = this.http.get('/api/WeatherForecast');

{{ weather$ | json }}

No comments:

Post a Comment