I'm using $resource to manipulate my data. I would like to convert dates straigt away into date objects when I fetch them. It just makes it easier to work with datepicker etc.
My factory:
AppFactories.factory("Books", ['$resource' ,function($resource){
return $resource(
"/books/:id",
{id: "@id" },
{
"update": {method: "PUT", isArray: true },
"save": {method: "POST", isArray: true },
}
);
}]);
Can I create a function within the factory to convert dates from the database into a date object and vice versa when I post/update?
It would be really nice to integrate it right into the factory so that I can reuse it.
Instead of doing this on every resource you could apply it to the underlying $http as described in more detail here.
app.config(["$httpProvider", function ($httpProvider) {
$httpProvider.defaults.transformResponse.push(function(responseData){
convertDateStringsToDates(responseData);
return responseData;
});
}]);
The $resource
action object can have a property called interceptor
, which allows you to define an interceptor
object for this particular action (like with $http interceptors). You can define response
and responseError
properties for this interceptor. This way you can define some date parsing functionality for the actions the resource offers:
function parseResponseDates(response) {
var data = response.data, key, value;
for (key in data) {
if (!data.hasOwnProperty(key) && // don't parse prototype or non-string props
toString.call(data[key]) !== '[object String]') continue;
value = Date.parse(data[key]); // try to parse to date
if (value !== NaN) data[key] = value;
}
return response;
}
return $resource('/books/:id', {id: '@id'},
{
'update': {
method: 'PUT',
isArray: true,
interceptor: {response: parseResponseDates}
},
....
}
);
Hope this helps!
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With