Home > Net >  How to automatically convert YYYY-MM-DD strings from HTTP responses to Date?
How to automatically convert YYYY-MM-DD strings from HTTP responses to Date?

Time:08-06

In an Angular application, I'm getting a JSON from the server which contains a date, the date is like "2022-08-05" in developers tools -> network -> response.

Using an HTTP request, I'm putting the JSON inside the following interface:

export interface Movie {
  id: number
  title: string
  releaseDate: Date
  director: string
  description: string
}

When performing movie.releaseDate.getFullYear(), the following error shows in console: movie_r2.releaseDate.getFullYear is not a function

Then, I tried using a class with a property decorator but that doesn't work either.
Afterwards, I tried with getter and setter, but I get the same error.


// function asDate(target: Object, propertyKey: string) {
//   let value: string;
//   console.log("asDate() called");
//
//   Object.defineProperty(target, propertyKey, {
//     get: () => new Date(value),
//     set: (newValue) => value = newValue
//   });
// }

export class Movie {
  id: number
  title: string
  // @asDate
  private _releaseDate: Date
  director: string
  description: string


  constructor(id: number, title: string, releaseDate: Date, director: string, description: string) {
    console.log("constructor called");
    this.id = id;
    this.title = title;
    this._releaseDate = new Date(releaseDate);
    this.director = director;
    this.description = description;
  }

  get releaseDate(): Date {
    console.log("called getter");
    return new Date(this._releaseDate);
  }

  set releaseDate(releaseDate: Date) {
    console.log("called setter");
    this._releaseDate = new Date(releaseDate);
  }
}

Why are the console.log statements in the descriptor, constructor, getter, setter never called?

How to write some code at the level of the Movie class/interface/type such that, for any HTTP request which contains the type Movie, the field releaseDate will contain an object of type Date (and not a string)? I have more than one HTTP request with a Movie interface/class, so I'm interested in a solution that is independent of the HTTP request. Meaning that I write it once, and it works at any request.

CodePudding user response:

Forcing type any (that is what comes from http request) to your Type (interface or class) doesn't mean it will automatically convert any fields or even check if the assumption is correct.

In order to actually convert string to Date, you need to invoke the transformation code. For instance you can do the following when working with interface:

this.http.get('api.url').pipe(
  map(response => ({
    ...response, 
    releaseDate: new Date(response.releaseDate)
  }),
);

or the following, when using the class:

this.http.get('api.url').pipe(
  map(response => new Movie(response.id, response.releaseDate, ...)),
);

to be 100% on the same page: How does this work:

// this is a type that comes from API:
export interface MovieDTO {
  id: number
  title: string
  releaseDate: string
  ...
}

// this is a type you would like to have after the transformation:
export interface Movie {
  id: number
  title: string
  releaseDate: Date
  ...
}

// in order to map MovieDTO to Movie you need to actually invoke the mapping. 
// to do so, you can use for instance the spread operator, as follows:

// some.service.ts
getMovie(): Observable<Movie> {
  return this.http.get<MovieDTO>('api.url').pipe(
    map(response => ({
      ...response, 
      releaseDate: new Date(response.releaseDate)
  }),
);

}

Or if you want to go with the class approach, here is how you can make it working:

// this is a type that comes from API:
export interface MovieDTO {
  id: number
  title: string
  releaseDate: string
  ...
}

// this is your class:
export class Movie {
  public id: string;
  public title:string;
  public releaseDate: Date;

  constructor(data: MovieDTO){
    this.id = data.id;
    this.title = data.title;
    this.releaseDate = new Date(data.releaseDate);
  }
}

// in order to map MovieDTO to Movie you need to actually create the object by calling new(...). 

// some.service.ts
getMovie(): Observable<Movie> {
  return this.http.get<MovieDTO>('api.url').pipe(
    map(response => new Movie(response),
  );
}

  • Related