23

I have an Angular 2 application. A service is requests data from an api that returns the results like the following:

{
    "data":[
        {"id":1,"timestamp":"2016-04-17T19:52:53.4510935+01:00","sourceDatabaseServer":"127.0.0.1","sourceDatabaseName":"Database1","targetDatabaseServer":"192.168.99.101","targetDatabaseName":"Database2"},
        {"id":2,"timestamp":"2016-04-17T19:52:53.4510935+01:00","sourceDatabaseServer":"127.0.0.2","sourceDatabaseName":"Database3","targetDatabaseServer":"192.168.99.102","targetDatabaseName":"Database4"},
        {"id":3,"timestamp":"2016-04-17T19:52:53.4510935+01:00","sourceDatabaseServer":"127.0.0.3","sourceDatabaseName":"Database5","targetDatabaseServer":"192.168.99.103","targetDatabaseName":"Database6"}
    ]
}

My Angular 2 service looks like this (I've cut the error handling for brevity as we're on the happy path here):

getList() : Observable<SomeModel[]> {
    return this._http.get(this._getListUrl).map(this.extractData);
}

 private extractData(res: Response) {
    return res.json().data || {};
}

and my component like this:

results: SomeModel[];
errorMessage: string;
ngOnInit() {
    this._someService.getList()
        .subscribe(
        results => this.results = results, 
        error => this.errorMessage = <any>error);
}

and my model like this:

export class SomeModel {

    constructor(
        public id: number,
        public timestamp: Date,
        public sourceDatabaseServer: string,
        public sourceDatabaseName: string,
        public targetDatabaseServer: string,
        public targetDatabaseName: string
    ) { }
}

Everything looked like it was working however when I tried to display timestamp using the DatePipe like so {{item.timestamp | date:'short'}} the application blows up with the following error message:

Invalid argument '2016-04-17T19:40:38.2424240+01:00' for pipe 'DatePipe' in [{{result.timestamp | date:'short'}}

After some investigation I believe that timestamp is not actually being converted to the Date type but is instead just being set a string. I'm guessing this is becuase the Date type isn't known at the time Response.json() is called? or am I missing something else entirely? Is there a fix or work around for this?

rcarrington
  • 1,485
  • 2
  • 12
  • 23
  • 1
    When you do a `.json()` that will not transform a string (even if it's in some date format) to an actual date object. It'll keep being a string. so you need to do `new Date(herePassTheString)` – arg20 Apr 17 '16 at 19:32
  • 1
    If you are only displaying it, the DatePipe in Angular 2 can now use the ISO string format. https://angular.io/docs/ts/latest/api/common/index/DatePipe-pipe.html I do not know if this has always been the case, but it is now. ;) – Derrick Feb 08 '17 at 13:41

4 Answers4

24

I would map the string field to a date one:

getList() : Observable<SomeModel[]> {
  return this._http.get(this._getListUrl).map(this.extractData);
}

private extractData(res: Response) {
  var data = res.json().data || [];
  data.forEach((d) => {
    d.timestamp = new Date(d.timestamp);
  });
  return data;
}
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
  • Yep, best approach... everything after that is in alignment then! And here's for an alternative format :-), `d.timestamp = new Date(parseInt(d.timestamp.substr(6)));` – Tim Harker Apr 05 '17 at 18:49
  • 1
    Why do I get an error on this line: var data = res.json().data || []; when I try to use this code? "Property data does not exist on type Promise" – Aaron Jan 03 '18 at 00:29
6

The date pipe only accepts Date values not string values.

See How to get Date object from json Response in typescript for how to convert dates in JSON.

Alternatively you can create your own string-to-date conversion pipe

@Pipe({name: 'toDate'})
export class StringToDate implements PipeTransform {
  transform(value, [exponent]) : number {
    if(value) {
      return new Date(value);
    }
  }
}

and then use it like

{{item.timestamp |toDate | date:'short'}}

Hint: don't forget to add the pipe to pipes: [StringToDate] on the @Component(...) decorater where you want to use it.

See also

Community
  • 1
  • 1
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • This is actually an angularJS (angular 1) response. Angular 2 now supports a variety of formats, including the correct ISO string format. https://angular.io/docs/ts/latest/api/common/index/DatePipe-pipe.html – Derrick Feb 08 '17 at 13:40
  • 1
    It's not a AngularJS 1 answer (don't know 1.x), but it's almost a year old and a lot has changed since then. I'll update when I'm back. Thanks for the note. – Günter Zöchbauer Feb 08 '17 at 14:46
  • Ah, sorry. You are correct. I see your @Pipe in there now. I was actually confused since the docs for angular 1 were the ref links. https://angular.io/docs/ts/latest/api/common/index/DatePipe-pipe.html – Derrick Feb 09 '17 at 17:02
  • Best resources for this: https://angular.io/guide/pipes here: https://angular.io/api/common/DatePipe and here: https://loiane.com/2017/08/angular-tips-formatting-dates-with-a-custom-date-pipe-dd-mm-yyyy/ – alex Dec 14 '18 at 09:37
3

JSON does not provide any date specification, so it is entirely up to how it is serialized/deserialized.

You could use reviver parameter of JSON.parse:

this._http.get(this._getListUrl).map(res => JSON.parse(res.text(), this.reviver));

reviver(key, value):any
{
    if('timestamp' === key){
        //you can use any de-serialization algorithm here
        return new Date(value);
    }
    return value;
}
kemsky
  • 14,727
  • 3
  • 32
  • 51
3

Try out my example if its work for you. And here is the docs for Date and Pipe.

<span>Date : {{announcement.createdAt | date:'EEE, d MMM,y'}}</span>
<span>Time : {{announcement.createdAt | date:'shortTime'}}</span>

Output :

Date : Tue, 20 Dec,2016    
Time :  2:22 PM
Ajay Gupta
  • 2,867
  • 23
  • 28