【问题标题】:Angular 2 "time ago" pipeAngular 2“前段时间”管道
【发布时间】:2016-08-08 08:54:23
【问题描述】:

我正在尝试为我的 Angular 2 应用程序创建一个“time ago”管道。

它应该将日期转换为字符串,例如“5 分钟前”或“60 秒前”。到目前为止它工作得很好,但在第一次计算后它不会更新。例如,如果给定的日期是 5 秒前,它会显示“5 秒前”,但此后不会更改。

我已经尝试将管道的“纯”值设置为 false,但这没有帮助。

这是我的代码:

import {Pipe, PipeTransform} from 'angular2/core';

@Pipe({
  name: 'messageTime',
  pure: false
})
export class MessageTimePipe implements PipeTransform {
  transform(value: Date, []): string {
    var result: string;

    // current time
    let now = new Date().getTime();

    // time since message was sent in seconds
    let delta = (now - 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;
  }
}

我正在使用这样的过滤器:

打字稿:

import {Component, Input} from 'angular2/core';
import {MessageTimePipe} from '../../pipes/message-time.pipe';

@Component({
  selector: 'message-item',
  pipes: [MessageTimePipe],
  templateUrl: 'build/components/message-item/message-item.component.html'
})
export class MessageItemComponent {
  @Input()
  message: JSON;

  date: Date;

  ngOnInit() {

   this.date = new Date(2016, 3, 16, 12, 49, 10);
  }
}

HTML:

<p class="time">
  {{ date | messageTime }}
</p>

【问题讨论】:

  • 你能不能也展示一下你是如何使用这个管道的?

标签: date angular pipe


【解决方案1】:

终于搞定了,很有挑战性,需要调整间隔:)

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;
        });
    };
}

【讨论】:

  • 完美运行,谢谢!我认为将所有代码都放在管道中并且不必在组件中使用超时功能会更好。但它也更有效率吗?我不太明白代码。
  • 我正在扩展async 管道,它能够跟踪可观察对象并更新视图。以asyncsource为例,从头开始实现这样的管道也许会更好,但我没有足够的时间。
  • 它是否适用于 Angular 2 稳定版?我在这一行遇到错误:export class MessageTimePipe extends AsyncPipe => Type 'any' is not a constructor function type
  • @kemsky 您可能希望将distinctUntilChanged 运算符添加到可观察管道中,因此可观察对象仅在更改时才会发出新值。
【解决方案2】:

接受的答案不适用于 Angular 7+。

我遵循这个答案并为越南语定制。

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

我为谁担心。

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;
        }
    }

【讨论】:

    【解决方案3】:

    以下库在英语中可以完成相同的工作,并且可以分叉以更改语言或支持不同的语言:

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

    npm install time-ago-pipe --save
    

    然后在你要使用的@NgModule中:

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

    在模板中:

    <span>{{your_date | timeAgo}}</span>
    

    【讨论】:

    • 嘿,在那个链接中,应该在哪个文件映射和包中?
    • 它不适用于 ionic2
    • @MuneemHabib 我在 Ionic 2 应用程序中使用它。
    • 或者除非你来自非英语国家并且那个图书馆不适合你......
    • 简单且非常有用。使用 Angular 5。
    【解决方案4】:

    使用 ngx-moment https://github.com/urish/ngx-moment,与 Momentjs 的管道集成,支持 i18n

    【讨论】:

    • 不幸的是,Moment.Js 是一个处于维护模式的遗留项目。见Project Status
    【解决方案5】:

    moment.js 解决方案。在 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;
            }
        }
    }
    

    【讨论】:

      【解决方案6】:

      在 AngularJs2 中获取 Time-Ago

      您需要运行此命令 npm install time-ago-pipe --save 以在您的 Angular 应用程序中安装 time-ago npm package package ex : PS D:\D\TimeAgo> npm install time-ago-pipe --save. 运行此命令后,将添加软件包。 Image shows how to add import {TimeAgoPipe} from 'time-ago-pipe'; in app.module and put TimeAgoPipe in Declarations[传递来自组件的输入o/p

      【讨论】:

      【解决方案7】:

      正如 Thierry 指出的那样,您需要更新“日期”引用以触发 Angular2 的更改检测,例如使用 setTimeout(()=&gt;this.date=new Date(), period)

      您真的需要每秒更新一次吗? 根据您的用例,每 60 秒更新一次可能就足够了,并且可能会在前 60 秒显示“刚刚”或“不到一分钟前”。

      但如果你真的想要秒数,你只需要在前 60 秒内每秒更新一次。然后setTimeout(1000) 可以变为setTimeout(60000),以最大限度地减少开销。

      【讨论】:

        【解决方案8】:

        我认为这与您的管道无关,而是与 Angular2 检测更改的方式有关。它根据引用检测更改,即绑定引用是否更改,而不是其中的元素是否更新。

        请参阅以下示例:

        @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);
          }
        }
        

        请参阅 thisx plunkr:https://plnkr.co/edit/kJdi1wx0iu9tDx8yTmRx?p=preview

        【讨论】:

        • 谢谢,我已经认为它必须通过更改检测来做一些事情。但是我怎样才能让我的代码工作呢?每秒重复更新值?这可能会奏效,但我更喜欢更优雅的解决方案。
        猜你喜欢
        • 1970-01-01
        • 2017-12-25
        • 2017-03-11
        • 2023-03-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-11
        • 2016-12-06
        相关资源
        最近更新 更多