I'm displaying a list of items with their relative creation dates using moment.js .fromNow() like so:
getRelativeDate(date: string): string {
return moment(date, 'YYYY-MM-DD HH:mm:ss').fromNow();
}
<div class="created">{{getRelativeDate(item.created)}}</div>
And here's what it looks like:
But unless I interact with the item somehow it stays the same after initial load, which makes sense, since data isn't changing, current time is.
How can I force-refresh these values? Every minute would suffice.
I'm displaying a list of items with their relative creation dates using moment.js .fromNow() like so:
getRelativeDate(date: string): string {
return moment(date, 'YYYY-MM-DD HH:mm:ss').fromNow();
}
<div class="created">{{getRelativeDate(item.created)}}</div>
And here's what it looks like:
But unless I interact with the item somehow it stays the same after initial load, which makes sense, since data isn't changing, current time is.
How can I force-refresh these values? Every minute would suffice.
Share Improve this question asked Nov 6, 2017 at 23:54 abfaridabfarid 5556 silver badges13 bronze badges 1-
This will depend on if your
item
data is async or not. You can create asetInterval()
inngOnInit()
if the data is not async, if the data is async you can create thesetInterval()
after the data is fetched. – Z. Bagley Commented Nov 7, 2017 at 0:02
4 Answers
Reset to default 5Surprised no one mentioned creating a pipe for this?
import { Pipe, PipeTransform } from '@angular/core';
import moment from 'moment';
@Pipe({
name: 'dateTimeFormat',
pure: false,
})
export class DateTimeFormatPipe implements PipeTransform {
transform(date: string, format: string = 'YYYY-MM-DD HH:mm:ss'): string {
return moment(date, format).fromNow();
}
}
This keeps your ponent clean by offloading the logic into a Pipe.
<ul>
<li *ngFor="let date of dates">
{{ date | dateTimeFormat:'YYYY-MM-DD HH:mm:ss'}}
</li>
</ul>
Whats cool is the format is optional and can be reused throughout your application.
Then to update the ponent periodically refresh call setInterval
.
You also need to invoke ChangeDetectorRef and call detectChanges
based on your ChangeDetectionStrategy
. Better yet, detach the ponent pletely.
Live Example https://stackblitz./edit/angular-cli-moment-pipe
Helpful set of Moment Pipes https://github./urish/angular2-moment/
Go with a setInterval
.
We'll need the ChangeDetectorRef
to do this.
import {OnInit, OnDestroy, ChangeDetectorRef} from '@angular/core';
export class Listponent implements OnInit, OnDestroy {
intervalHolder: any;
constructor(private _changeDetectorRef: ChangeDetectorRef) {
}
ngOnInit(): void {
this.intervalHolder = setInterval(() => {
// Let's refresh the list.
this._changeDetectorRef.markForCheck();
}, 1000 * 60); // 1 minute
}
ngOnDestroy(): void {
clearInterval(this.intervalHolder);
}
}
If your data isn't async you can create a setInterval()
in your ngOnInit()
and then clear it ngOnDestroy()
. Assuming your created dates are stored in an array named items
under key created
:
ponent.ts
myFromNowInterval: any;
myFromNow = Array(this.items.length).fill('');
ngOnInit() {
this.myFromNowInterval = setInterval( () => {
this.items.forEach( (item, index) => this.myFromNow[index] = this.getRelativeDate(item) )
}, 60000);
}
ngOnDestroy() {
clearInterval(this.myFromNowInterval);
}
ponent.html
<div *ngFor="item of items; let i = index;">
<div class="created">{{ myFromNow[i] }}</div>
</div>
This will update myFromNow
array every minute, and aligns the index of your new array to the DOM.
I think it's sufficient to just put an observable interval on the ponent, to trigger change detection.
triggerCD$;
ngOnInit() {
this.triggerCD$ = Observable.interval(60000)
}
Testing with this
<span>{{ now() }}</span>
now() {
const now: any = new Date();
return (now - this.start)
}
With no trigger, display does not change.
With trigger, now() updates on the template (although no direct link to trigger).