I wanted to use Angular.js's ngResource ($resource), but I also wanted my date parsing not to interfere with anything else in $http, and the action's transformResponse option seemed more appropriate than the interceptor option. @jakee's parseResponseDates is nice because it's general purpose, but it's more brute-force than I wanted; I know exactly what fields can be dates in my models, and I only want to parse those fields as dates.
The documentation says the default transformResponse is just angular.fromJson but that's not quite true; it also strips the JSON vulnerability protection prefix, )]}',\n, before running angular.fromJson (http.js source reference). Since $resource doesn't readily expose each action's transformResponse array, you have to handle extending the default transformResponse manually, by injecting $http and then calling $http.defaults.transformResponse.concat(your_transformer). The solution I arrived at, in the context of your example, would look something like this:
AppFactories.factory("Books", function($resource, $http) {
var raiseBook = function(book) {
// "created_at" is declared as NOT NULL in the database
book.created_at = new Date(book.created_at);
// "completed_at" is a NULL-able field
if (book.completed_at) {
book.completed_at = new Date(book.completed_at);
}
return book;
};
var raiseBooks = function(books) {
return books.map(raiseBook);
};
return $resource("/books/:id", {
id: "@id"
}, {
update: {
method: "PUT",
isArray: true,
// some_array.concat(...) creates and returns a new Array,
// without modifying the original some_array Array
transformResponse: $http.defaults.transformResponse.concat(raiseBooks),
},
save: {
method: "POST",
isArray: true,
transformResponse: $http.defaults.transformResponse.concat(raiseBooks),
},
});
});
Note that Date.parse(...) returns a Number (milliseconds since the UNIX epoch), so if that's what you want, you'll have to change the new Date(...) calls. But since the original question calls for date objects, new Date(...) is probably what you want.