angular - تابع Interval باعث بارگذاری بینهایت رابط کاربری میشود مگر اینکه من «/index.html» را در URL تایپ کنم


من در حال تلاش برای ساخت یک ساعت دیجیتال ساده در برنامه انگولارم هستم، اما در حال حاضر فقط دارم یک شمارنده نمایش میدهم تا بفهمم آنچه که در حال رخ دادن است چیست:

در clock.component.html:

<div class="time" id="time">{{ secondsPassed }}</div>

در clock.component.ts:

@Component({
  selector: 'app-clock',
  standalone: true,
  imports: [],
  templateUrl: './clock.component.html',
  styleUrl: './clock.component.scss'
})
export class ClockComponent implements OnInit, OnDestroy {

  secondsPassed: number;
  timer: any;

  constructor() {
    this.secondsPassed = 0;
  }

  ngOnInit(): void {
    this.startTimer();
  }

  ngOnDestroy(): void {
    if (this.timer) {
      clearInterval(this.timer);
    }
  }

  startTimer() {
    this.timer = setInterval(() => {
      this.secondsPassed++;
      console.log(this.secondsPassed);
    }, 1000)
  }
}

هر وقت من دستور ng serve را اجرا میکنم و به localhost:4200 میروم، همیشه در حلقه بارگذاری چرخان گیر میکند مگر اینکه خودم به صورت دستی localhost:4200/index.html را تایپ کنم، در آن صورت تایمر به درستی نمایش داده میشود و بهروزرسانی میشود.

من تعداد زیادی روش مختلف را امتحان کردم، از جمله اشتراک RxJS و async pipe، اما هیچکدام جواب ندادند. حتی سعی کردم خارج از زون Angular اجرا کنم، اما با آن، تایمر بهروزرسانی نمیشود.

یک چیز واقعاً عجیب که متوجه شدم این است که هرگاه تغییرات را در کدم ذخیره میکنم و در حالی که سرور محلی Angular در حال اجراست، بازسازی صورت میگیرد، به نظر میرسد که دو تایمر در حال اجرا هستند، بنابراین مطمئن نیستم که آیا ngOnDestroy فراخوانی میشود و تایمر اول پاک نمیشود؟ من تازه با Angular آشنا هستم و نمیدانم آیا این موضوع به مشکلم مربوط است یا نه.

ممنون از کمکت!

پاسخ شماره ۱

به دلایلی، پاسخ این سؤال جواب داد. نمیدانم چرا، اما حداقل مشکل من را حل کرد :)

پاسخ شماره ۲

من همان مشکل را داشتم و پیدا کردن راه حل طول زیادی برایم طول کشید.

با معرفی SSR در Angular 17، رندر صفحات در سرور انجام میشود، که منجر به تولید محتوای HTML اولیه میشود که شامل وضعیت اولیه صفحه است. این باعث میشود که رندر تکمیل نشده باشد در حالی که یک تایمر یا فاصله زمانی در حال اجرا است.

این مشکل با روش afterNextRender برطرف میشود، که باید در یک زمینهی قابل تزریق فراخوانی شود، ترجیحاً در constructor.

afterNextRender(() => {
  setInterval(() => {
    console.log('Interval triggered');
    // Put your code here
    ngZone.run(() => {
      // Put your ui changes here
    });
  }, 10000);
}, {phase: AfterRenderPhase.Write});

تغییرات رابط کاربری باید دوباره در محیط انگولار انجام شوند.

مطمئن شوید که از RenderPhase مناسب استفاده میکنید.

پاسخ کامل به سؤال شما:

import {afterNextRender, AfterRenderPhase, Component, NgZone, OnDestroy, OnInit} from "@angular/core";

@Component({
  selector: 'app-clock',
  standalone: true,
  imports: [],
  templateUrl: './clock.component.html',
  styleUrl: './clock.component.scss'
})
export class ClockComponent implements OnInit, OnDestroy {

  secondsPassed: number;
  timer: any;

  constructor(private ngZone: NgZone) {
    this.secondsPassed = 0;
    afterNextRender(() => {
      this.startTimer();
    }, {phase: AfterRenderPhase.Write});
  }

  ngOnInit(): void {
    //this.startTimer();
  }

  ngOnDestroy(): void {
    if (this.timer) {
      clearInterval(this.timer);
    }
  }

  startTimer() {
    this.timer = setInterval(() => {
      this.ngZone.run(() => {
        this.secondsPassed++;
      });
      console.log(this.secondsPassed);
    }, 1000)
  }
}