4

Task.ts:

export class Task {
    name: string;
    dueDate: Date;
}

tasks.service.ts:

@Injectable()
export class TasksService {

    constructor(private http: HttpClient) { }

    getTasks(): Observable<Task[]> {
        return this.http.get<Task[]>(`${WEBAPI_URL}/Tasks`);
    }
}

The Task objects I get back from getTasks() have their dueDate field assigned but the value is of type string instead of Date like I would expect.

Some searching lead me to this issue on the Angular github which made clear to me that HttpClient has no intent of properly parsing my object. Unfortunately the issue didn't give helpful guidance about what I should actually be doing to get my Date object. What do I do?

Kyle V.
  • 4,752
  • 9
  • 47
  • 81

3 Answers3

4

You have several options here.

1) You can deal with the date as a string in the UI. So change the definition of Task to retain the date as a string and work with it that way, converting it to a date for calculations as needed.

2) You can map each object coming back from the Http request to a Task object. Something like this:

getTasks(): Observable<Task[]> {
    return this.http.get<Task[]>(`${WEBAPI_URL}/Tasks`)
        .pipe(
            map(items => {
                const tasks: Task[] = [];
                return items.map(
                    item => {
                        item.dueDate = new Date(item.dueDate);
                        return Object.assign(new Task(), item);
                    });
            }),
            tap(data => console.log(JSON.stringify(data))),
            catchError(this.handleError)
        );
}

This also have the benefit of having actual task objects in your array, meaning that if you ever add Task methods or getters/setters they will be correctly associated with your tasks array.

EDIT: It may be better to build a utility class that handled the serialization/deserialization of your objects. Then the above code would look more like this:

getTasks(): Observable<Task[]> {
    return this.http.get<Task[]>(this.url)
        .pipe(
            map(TaskSerializer.serialize),
            catchError(this.handleError)
        );
}
DeborahK
  • 57,520
  • 12
  • 104
  • 129
  • This seems like a reasonable solution but I can see it getting out of hand when the class has lots of properties of different, more complex types. I would be manually parsing everything and any refactoring I do would break the parser. – Kyle V. Feb 23 '18 at 17:37
  • 1
    It would be best to put this code into its own method. Consider building a utility class with methods to "serialize" and "deserialize" the data. – DeborahK Feb 23 '18 at 17:54
  • 1
    And when you think about it ... that would be why the HttpClient could not do this automatically. It could not know about all of the complex types and how to map them appropriately. However, there are mapping tools out there that you could look at. I have not used any but googling `Angular 4 mapper` brought up a few options. – DeborahK Feb 23 '18 at 18:10
0

declare it as a date in the component like this:

example.component.ts

constructor(private taskService: TaskService) {
}

ngOnInit() {
    this.taskService.getTaksks().subscribe(response => {
        tempValue = response.body;
        tempValue.dueDate = new Date(tempValue.dueDate.format('MM-DD-YYYY');
    });
}

OR save it as an instant

Task.ts

export class Task {
    name: string;
    dueDate: Instant;
}

I would suggest doing the first way. I would also suggest looking at the moment.js library

EDIT: I would declare it as a Date object and let it store it as a string on the db. That is how I have seen most use cases for dealing with dates and it is how everyone in my company has dealt with dates. 99% of the time you just want the month/day/year so it makes sense that you will store only that on the db, but it is a little cumbersome to format it to a date object on the ui side.

rhavelka
  • 2,283
  • 3
  • 22
  • 36
  • Won't `response.body` be the array of tasks? It seems that you would need to add a `.foreach` and loop through all of them? – DeborahK Feb 23 '18 at 17:19
  • depends on your web service call. If I am expecting an object, `response.body` returns my object from the rest api perfectly, If I am expecting an array of objects it returns an array of objects. If you have a different configuration then you do you. The main point is the `new Date()` code that formats your response. – rhavelka Feb 23 '18 at 17:22
  • What is an `Instant`? – Kyle V. Feb 23 '18 at 17:33
  • Instant is like a timestamp. So maybe it isn't the best for this situation https://stackoverflow.com/questions/6627289/what-is-the-most-recommended-way-to-store-time-in-postgresql-using-java – rhavelka Feb 23 '18 at 17:42
0

I found a not so heavy interceptor, allowing to have directly correct Date in objects when using HTTP calls in Angular. You can find it here: https://dev.to/imben1109/date-handling-in-angular-application-part-2-angular-http-client-and-ngx-datepicker-3fna

Please note that the only thing I had to change was the regex, in order to make the Z at the end optional and I use the DateTimeFormatter.ISO_DATE_TIME format on server side.

Chavjoh
  • 466
  • 5
  • 13