Search code examples
angularviewtimersetintervalcountdown

Angular 17 does not update view using setInterval with NG0500 error in console


I am building a countdown timer component and trying to display updated 'seconds' value within every second, but I do not see any update on UI

When I do not use outside zone attitude, then page is just blank and loading infinitely.

.ts file

import { Component, ElementRef, NgZone, OnInit, ViewChild } from '@angular/core';

@Component({
  selector: 'app-countdown',
  templateUrl: './countdown.component.html',
  styleUrl: './countdown.component.css'
})

export class CountdownComponent implements OnInit {

  static initialized: boolean = false;

  weddingDate: Date = new Date('2024-08-17T15:00:00');

  constructor(private zone: NgZone) {}
  @ViewChild('seconds') seconds: ElementRef;

  ngOnInit(): void {
    if(!CountdownComponent.initialized) {
      this.countdown(this.zone);
      CountdownComponent.initialized = true;
    }
  }

  countdown(zone: NgZone) {
    zone.runOutsideAngular(() => {
      setInterval(() => { 
        this.seconds.nativeElement.innerHTML = (this.weddingDate.getTime() - new Date().getTime()) / 1000;
        console.log(this.seconds.nativeElement.innerHTML);
      }, 1000);
    })
  }
}

html file

<div #seconds> </div>

Solution

  • The NG0500 error means your project uses Server Side Rendering (SSR). It means your application runs everything on the server first waits for asynchronous tasks to complete and then sends it to the client where the application continues to execute.

    Running setInterval prevents the server from completing rendering as there is always an asynchronous task to execute. That is why wrapping it into zone.runOutsideAngular makes the difference, as Angular does not watch for it anymore, but this wrapping also makes these changes invisible for Change Detection on the client side which is why the counter is not updating there.

    If you want to use SSR and make your code work, you can add a check for the platform, and start the counter only on the client:

    @Component({
      selector: 'app-countdown',
      standalone: true,
      template: `<div>{{ secondsLeft }}</div>`,
    })
    export class CountdownComponent {
    
      weddingDate: Date = new Date('2024-08-17T15:00:00');
      secondsLeft: number;
    
      constructor(@Inject(PLATFORM_ID) private platform: Object) {}
    
      ngOnInit(): void {
        if (!isPlatformServer(this.platform)) {
          this.countdown();
        }
      }
    
      countdown() {
        setInterval(() => {
          this.secondsLeft = (this.weddingDate.getTime() - new Date().getTime()) / 1000;
        }, 1000);
      }
    
    }
    
    

    Please note that I also removed updating element's innerHTML as this is a bad practice in Angular in general, and causes NG0500 error. It is always better to use bindings and avoid direct DOM manipulations.


    I would strongly advise you not to use SSR if you don't fully understand how it affects your application. The simplest way to disable it is to change angular.json file by removing the following lines in architect > build > options section:

    "prerender": false,
    "ssr": {
      "entry": "server.ts"
    }
    

    It disables SSR and you may want to remove some files related to SSR (they have server as a file name suffix).