Angular 2 Date deserialization

2020-01-30 08:29发布

问题:

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?

回答1:

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


回答2:

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

  • https://github.com/angular/angular/pull/8038
  • https://github.com/angular/angular/pull/7794


回答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


回答4:

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