The AngularJS $http service is great for most any communication you need to do with the web services that are likely backing your single-page-application. However one place where it doesn’t quite do what you usually want is in the deserialization of dates.
Using $http to post a blog entry for example is pretty easy:
myPost = id: 123 title: 'Using HTTP Interceptors to Deserialize Dates' status: 'draft' creationDate: new Date() body: 'TBD' $http.post('/api/blog', myPost) |
This results in the following JSON being posted to the server:
{ "id": 123 "title": "Using HTTP Interceptors to Deserialize Dates", "status": "draft", "creationDate": "2014-08-13T10:13:39.399Z", "body": "TBD" } |
This is great. Notice that the service has automatically serialized the creation date to an ISO-8601 compliant string. I couldn’t ask for more… or could I?
What if I want to now get that post back:
$http.get("/api/blog/123").success (blog) -> console.log blog.creationDate console.log typeof blog.creationDate |
Assuming that the endpoint returns the exact same JSON that was just posted, this code will output:
2014-08-13T10:13:39.399Z string |
This is not the end of the world but it may not be what one is expecting. The expectation is usually that if I put a Date instance into a serialization function, then I get a Date instance out of the matching deserialization function.
Fortunately, this situation is easily remedied with a Angular http interceptor. Angular’s $httpProvider service allows one to configure interceptors that can pre-process all out-going requests before they are sent and post-process all received responses before handing them back to the calling application. A response post-processor to find all ISO-8601 date strings and convert them to actual Date objects is easy to implement.
angular.module('myModule', []).config([ '$httpProvider' ($httpProvider) -> $httpProvider.interceptors.push [ -> #Matches YYYY-MM-ddThh:mm:ss.sssZ where .sss is optional iso8601RegEx = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/ response: (response) -> convertDates = (obj) -> for key, value of obj #If it is a string of the expected form convert to date type = typeof value if type is 'string' and iso8601RegEx.test value obj[key] = new Date(value) #Recursively evaluate nested objects else if type is 'object' convertDates value convertDates response.data response ] ]) |
And there you have it. Happy coding!