Reputation: 5307
I have a number of .net webApi actions returning dates that are deserialised into JSON. My angular application then consumes these date strings for display\edit or as the ngModel in a variety of date directives. Many of these directives require a javascript date object and not a string representation of a date. How do serialise the date string back to javascript date for all returned webapi data?
N.B. I have tried a variety of regExs that proport to be ISO 8601 compliant, but for every one I use there are a bunch of use cases that fail. The cases I require are as follows:
should convert date with time from string to date with time including seconds e.g. '2009-05-19T14:39:23'
should convert date with time to the millisecond from string to date with time with milliseconds e.g. '2016-06-09T13:02:39.957'
should convert date with time from string to date with time in UTC e.g. '2009-05-19T14:39Z'
should not convert a date which is part of a longer string e.g. 'Should not convert 2015-12-12T00:00:00Z as this is part of a longer string'
Upvotes: 2
Views: 2936
Reputation: 95
Here is an Angular 2+ solution. This assumes the date from the API is in ISO format:
In your Component:
// call to your data service
this.dataService.getSomeData().map(records => {
records.map(record => {
//call to shared service date conversion function
this.dataService.covertISOFieldstoDateObjects(record);
});
return records;
}).subscribe(x => {
// processed records will now have date objects for any date properties
this.records = x;
}, error => {
...
});
In your Service:
covertISOFieldstoDateObjects(currentObj) {
const entries = Object.entries(currentObj);
for (const [key, value] of entries) {
const isDate = moment(value, moment.ISO_8601).isValid();
if (isDate) {
currentObj[key] = moment(value as string, 'YYYY-MM-DD HH:mm'); // Use this to get UTC and ignore timezone (good when you need just the date)
currentObj[key] = moment(value as string, 'YYYY-MM-DD HH:mmZ'); // Use this to include timezone adjustment
}
}
return currentObj;
}
Upvotes: 1
Reputation: 5307
So, firstly in order to intercept and convert all string dates to javascript dates we need angular to do the interception and replacing. For this we can use an interceptor on the httpProvider to ensure our code runs on all returned http responses before any other code is executed.
var app = angular.module('app');
app.config(configureProviders);
configureProviders.$inject = ['$httpProvider'];
function configureProviders($httpProvider) {
configureHttp($httpProvider);
}
function configureHttp(httpProvider) {
// add all http interceptors
httpProvider.interceptors.push('dateDeserialiserInterceptor');
}
ok so now we have our interceptor registered, but we need some code to make it work:
(function () {
var module = angular.module('myApp.interceptors');
module.factory('dateDeserialiserInterceptor', function () {
function convertDateStringsToDates(input) {
// Ignore things that aren't objects.
if (typeof input !== "object") return input;
for (var key in input) {
if (!input.hasOwnProperty(key)) continue;
var value = input[key];
// Check for string properties which look like dates.
if (typeof value === "string" && (moment(value, moment.ISO_8601).isValid())) {
input[key] = moment(value, moment.ISO_8601).toDate();
} else if (typeof value === "object") {
// Recurse into object
convertDateStringsToDates(value);
}
}
};
return {
response: function (response) {
convertDateStringsToDates(response);
return response;
}
};
});
})();
So the above code was originally taken off the internet somewhere and had a manual regEx comparison. The regEx was described as ISO 8601 compliant, but I found a multitude of examples where it wasn't. This now relies on an Open Source library momentJs to determine if the date is ISO 8601 compliant and to do the conversion. If the date isn't compliant the string value is simply returned. ISO 8601 is a great standard to use (as it covers a multitude of cases) and is much better than hard coding any expected particular format which may lead you down a path of 'oh...it's missed one'.
At the moment this is parsing all returned response object values for potential dates. I thought about improving this by explicitly marking the request with the response properties that we expect to be dates. That way we wouldn't have to try\parse everything (which is slow) and also run the risk that something we don't want to convert gets converted. However that approach is a little messy and would litter many requests. I like this approach for now and it seems to work. Happy for it to be improved!
Upvotes: 2