Angular 2 “time ago” pipe

前端 未结 8 1419
余生分开走
余生分开走 2020-12-08 04:56

I am trying to create a \'time ago\' pipe for my Angular 2 application.

It should transform a date to a string such as \'5 minutes ago\' or \'60 seconds ago\'. It wo

相关标签:
8条回答
  • 2020-12-08 05:00

    The following library does equivalent job in English and could be forked to change the language or support different ones:

    https://www.npmjs.com/package/time-ago-pipe

    npm install time-ago-pipe --save
    

    Then in the @NgModule you want to use it in:

    import {TimeAgoPipe} from 'time-ago-pipe'
    
    @NgModule({
        imports: [... etc ...],
        declarations: [AppComponent, ...etc..., TimeAgoPipe],
        bootstrap: [AppComponent]
    })
    

    And in the template:

    <span>{{your_date | timeAgo}}</span>
    
    0 讨论(0)
  • 2020-12-08 05:00

    You need to update the 'date' reference to trigger Angular2's change detection, with a setTimeout(()=>this.date=new Date(), period) for instance, as Thierry pointed out.

    Do you really need an update every second? Updating every 60 seconds might be good enough depending on your use case, and could display 'just now' or 'less than a minute ago' for the first 60 seconds.

    But if you really want the seconds, you only need to update every seconds for the first 60 seconds. The setTimeout(1000) can then become setTimeout(60000), to minimizes overhead.

    0 讨论(0)
  • 2020-12-08 05:08

    Finally got it working, quite challenging and requires interval tweaking:)

    import {Pipe, ChangeDetectorRef} from 'angular2/core';
    import {Observable} from 'rxjs/Observable';
    import {AsyncPipe} from 'angular2/common';
    
    @Pipe({
        name: 'messageTime',
        pure: false
    })
    export class MessageTimePipe extends AsyncPipe
    {
        value:Date;
        timer:Observable<string>;
    
        constructor(ref:ChangeDetectorRef)
        {
            super(ref);
        }
    
        transform(obj:any, args?:any[]):any
        {
            if (obj instanceof Date)
            {
                this.value = obj;
    
                if(!this.timer)
                {
                    this.timer = this.getObservable();
                }
    
                return super.transform(this.timer, args);
            }
    
            return super.transform(obj, args);
        }
    
        private getObservable()
        {
            return Observable.interval(1000).startWith(0).map(()=>
            {
                var result:string;
                // current time
                let now = new Date().getTime();
    
                // time since message was sent in seconds
                let delta = (now - this.value.getTime()) / 1000;
    
                // format string
                if (delta < 10)
                {
                    result = 'jetzt';
                }
                else if (delta < 60)
                { // sent in last minute
                    result = 'vor ' + Math.floor(delta) + ' Sekunden';
                }
                else if (delta < 3600)
                { // sent in last hour
                    result = 'vor ' + Math.floor(delta / 60) + ' Minuten';
                }
                else if (delta < 86400)
                { // sent on last day
                    result = 'vor ' + Math.floor(delta / 3600) + ' Stunden';
                }
                else
                { // sent more than one day ago
                    result = 'vor ' + Math.floor(delta / 86400) + ' Tagen';
                }
                return result;
            });
        };
    }
    
    0 讨论(0)
  • 2020-12-08 05:09

    I think that it's not related to your pipe but to the way Angular2 detects changes. It detects changes based on references, i.e. if bound references change and not if elements in them are updated.

    See the following sample:

    @Component({
      selector: 'my-app',
      template: `
        <div>{{val | pdate}}</div>
      `,
      pipes: [ DatePipe ]
    })
    export class AppComponent {
      constructor() {
        this.val = new Date();
    
        setTimeout(() => {
          this.val = new Date(); // Updates view
        }, 1000);
    
        setTimeout(() => {
          this.val.setTime((new Date().getTime()); // Doesn't update view
        }, 2000);
      }
    }
    

    See thisx plunkr: https://plnkr.co/edit/kJdi1wx0iu9tDx8yTmRx?p=preview.

    0 讨论(0)
  • 2020-12-08 05:13

    A moment.js solution. Working/tested in Angular 6:

    import { ChangeDetectorRef, NgZone, Pipe, PipeTransform } from '@angular/core';
    import * as moment from 'moment';
    
    moment.locale("de"); // set your language
    
    @Pipe({
        name:'timeago',
        pure:false
    })
    export class TimeagoPipe implements PipeTransform {    
        private timer: number;
        constructor(private changeDetectorRef: ChangeDetectorRef, private ngZone: NgZone) {}
        transform(value:string) {
            this.removeTimer();
            let d = new Date(value);
            let now = new Date();
            let seconds = Math.round(Math.abs((now.getTime() - d.getTime())/1000));
            let timeToUpdate = (Number.isNaN(seconds)) ? 1000 : this.getSecondsUntilUpdate(seconds) *1000;
            this.timer = this.ngZone.runOutsideAngular(() => {
                if (typeof window !== 'undefined') {
                    return window.setTimeout(() => {
                        this.ngZone.run(() => this.changeDetectorRef.markForCheck());
                    }, timeToUpdate);
                }
                return null;
            });
    
        return moment(d).fromNow();
    
        }
        ngOnDestroy(): void {
            this.removeTimer();
        }
        private removeTimer() {
            if (this.timer) {
                window.clearTimeout(this.timer);
                this.timer = null;
            }
        }
        private getSecondsUntilUpdate(seconds:number) {
            let min = 60;
            let hr = min * 60;
            let day = hr * 24;
            if (seconds < min) { // less than 1 min, update every 2 secs
                return 2;
            } else if (seconds < hr) { // less than an hour, update every 30 secs
                return 30;
            } else if (seconds < day) { // less then a day, update every 5 mins
                return 300;
            } else { // update every hour
                return 3600;
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-08 05:17

    The accepted answer can not work with angular 7+.

    I followed this answer and customize for Vietnamese.

    https://stackoverflow.com/a/61341940/4964569

    I share for whom concern.

    import {Pipe, PipeTransform} from '@angular/core';
    
        @Pipe({
            name: 'dateAgo',
            pure: true
        })
        export class TimePipe implements PipeTransform {
        
            transform(value: any, args?: any): any {
                if (value) {
                    const seconds = Math.floor((+new Date() - +new Date(value)) / 1000);
                    if (seconds < 29) // less than 30 seconds ago will show as 'Just now'
                        return 'vừa mới đăng';
                    const intervals = {
                        'năm': 31536000,
                        'tháng': 2592000,
                        'tuần': 604800,
                        'ngày': 86400,
                        'giờ': 3600,
                        'phút': 60,
                        'giây': 1
                    };
                    let counter;
                    for (const i in intervals) {
                        counter = Math.floor(seconds / intervals[i]);
                        if (counter > 0){
                          return counter + ' ' + i + ' trước'; // singular (1 day ago)
                        }
                    }
                }
                return value;
            }
        }
    
    0 讨论(0)
提交回复
热议问题