Reputation: 31
This code works fine for displaying the timer, when i open it after the browser is loaded properly, in other words, if i open my profile section first and wait for the browser to finish loading and then navigate back to the home page, the countdown works fine . But if i reload the page from home page, it keeps on spinning.
`/* eslint-disable @typescript-eslint/no-unused-vars */
import {
Component,
OnInit,
OnDestroy,
ViewChild,
afterNextRender,
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { Carousel, CarouselModule } from 'primeng/carousel';
import { ButtonModule } from 'primeng/button';
import { TagModule } from 'primeng/tag';
import { AuctionService } from '../../../services/auction/auction.service';
import { interval, Subscription } from 'rxjs';
interface Auction {
id: number;
auction_end_date: string;
timeRemaining?: string;
[key: string]: any;
}
@Component({
selector: 'app-auctions',
standalone: true,
imports: [
CommonModule,
RouterModule,
CarouselModule,
ButtonModule,
TagModule,
],
templateUrl: './auctions.component.html',
styleUrl: './auctions.component.scss',
})
export class AuctionsComponent implements OnInit {
@ViewChild('carousel') carousel!: Carousel;
@ViewChild('carouselFuture') carouselFuture!: Carousel;
liveAuctions: Auction[] = [];
futureAuctions: Auction[] = [];
groupedFutureAuctions: any[][] = [];
responsiveOptions: any[] | undefined;
private countdownSubscription: Subscription | null = null;
constructor(private auctionService: AuctionService) {}
ngOnInit(): void {
this.responsiveOptions = [
{
breakpoint: '1199px',
numVisible: 1,
numScroll: 1,
},
{
breakpoint: '991px',
numVisible: 2,
numScroll: 1,
},
{
breakpoint: '767px',
numVisible: 1,
numScroll: 1,
},
];
this.fetchLiveAuctions();
this.fetchFutureAuctions();
}
startCountdown(): void {
this.countdownSubscription = interval(1000).subscribe(() => {
this.liveAuctions = this.liveAuctions.map((auction) => ({
...auction,
timeRemaining: this.calculateTimeRemaining(auction.auction_end_date),
}));
this.futureAuctions = this.futureAuctions.map((auction: Auction) => ({
...auction,
timeRemaining: this.calculateTimeRemaining(auction.auction_end_date),
}));
this.groupFutureAuctions();
});
}
groupFutureAuctions(): void {
this.groupedFutureAuctions = [];
for (let i = 0; i < this.futureAuctions.length; i += 4) {
this.groupedFutureAuctions.push(this.futureAuctions.slice(i, i + 4));
}
}
fetchLiveAuctions(): void {
this.auctionService
.getLiveAuctions()
.then((result: any) => {
// console.log('Result in fetch live auctions', result.data);
if (result?.data) {
this.liveAuctions = result.data.map((auction: Auction) => ({
...auction,
timeRemaining: this.calculateTimeRemaining(
auction.auction_end_date
),
}));
}
this.startCountdown();
})
.catch((error) => {
console.error('Error fetching live auctions:', error);
});
}
fetchFutureAuctions(): void {
this.auctionService
.getFutureAuctions()
.then((result: any) => {
console.log('Result in fetch future auctions', result.data);
if (result?.data) {
this.futureAuctions = result.data.map((auction: Auction) => ({
...auction,
timeRemaining: this.calculateTimeRemaining(
auction.auction_end_date
),
}));
}
this.groupFutureAuctions();
})
.catch((error) => {
console.error('Error fetching live auctions:', error);
});
}
calculateTimeRemaining(endDate: string): string {
if (!endDate) return 'Invalid date';
const end = new Date(endDate).getTime();
if (isNaN(end)) return 'Invalid date';
const now = Date.now();
const diff = end - now;
if (diff <= 0) return 'Auction ended';
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((diff % (1000 * 60)) / 1000);
return `${days}D ${hours}H ${minutes}M ${seconds}S`;
}
prevLive(event: Event): void {
event.preventDefault();
if (event instanceof MouseEvent || event instanceof TouchEvent) {
this.carousel?.navBackward(event);
}
}
nextLive(event: Event): void {
event.preventDefault();
if (event instanceof MouseEvent || event instanceof TouchEvent) {
this.carousel?.navForward(event);
}
}
prevFuture(event: Event): void {
event.preventDefault();
if (event instanceof MouseEvent || event instanceof TouchEvent) {
this.carouselFuture?.navBackward(event);
}
}
nextFuture(event: Event): void {
event.preventDefault();
if (event instanceof MouseEvent || event instanceof TouchEvent) {
this.carouselFuture?.navForward(event);
}
}
getSeverity(status: string) {
switch (status) {
case 'INSTOCK':
return 'success';
case 'LOWSTOCK':
return 'warning';
case 'OUTOFSTOCK':
return 'danger';
default:
return 'info';
}
}
trackByAuctionId(index: number, auction: any): number {
return auction.id;
}
}
`
so is there any other way to implement the counter, other than using interval??
Upvotes: 1
Views: 30
Reputation: 31
i have used window.setInterval for solving this issue and it is working well now, and instead of invoking the function inside onInit, now I have moved it into afterViewInit
ngAfterViewInit(): void {
this.startPersistentCountdown();
}
startPersistentCountdown(): void {
this.stopCountdown();
this.zone.runOutsideAngular(() => {
this.countdownTimer = window.setInterval(() => {
this.zone.run(() => {
this.updateAuctionTimers(this.liveAuctions);
this.updateAuctionTimers(this.futureAuctions);
this.groupFutureAuctions();
this.cdr.markForCheck();
});
}, 1000);
});
}
Upvotes: 0
Reputation: 56004
You might have SSR enabled, so it waits for the component to become stable which when using interval
does not happen, since it's an observable that does not end. So the solution is to execute the countdown logic only on the browser, using the afterNextRender
function (Which executes only on the browser).
ngOnInit() {
...
afterNextRender(() => {
this.fetchLiveAuctions();
});
}
Another option is to use Hybrid rendering which defines whether the route is to run on the client or the server.
ng add @angular/ssr --server-routing
Then in the routing you can define:
// app.routes.server.ts
import { RenderMode, ServerRoute } from '@angular/ssr';
export const serverRoutes: ServerRoute[] = [
{
path: 'countdown',
renderMode: RenderMode.Client,
},
...
Upvotes: 0