rsobon rsobon - 5 months ago 133
JSON Question

Parse date string to Date object when loading Angular UI Bootstrap Datepicker

I'm using Angular UI Bootstrap Datepicker:
https://angular-ui.github.io/bootstrap/#/datepicker

When I render form using data received from the server, there is problem with datetime fields. My input datepicker looks like this:

<form name="itemForm">
<input type="datetime" class="form-control" id="startedAt" name="startedAt"
ng-model="item.startedAt"
ng-click="open($event, 'startedAt')"
uib-datepicker-popup="yyyy-MM-dd"
is-open="datepickers.startedAt"
/>
</form>


My server returns response datetime as JSON string:

{
...
startedAt: "2015-05-29T02:00:00+0200"
}


When I assign response data to the model
$scope.item = response;
, datepicker input field is rendered correctly (correct date is selected and it's properly formatted in format I selected). The problem is that validation does not pass. I get:

itemForm.startedAt.$invalid == true


I noticed that data bound to the datepicker field should be
Date
object and not string (when I select new date from the datepicker,
$scope.item.startedAt
is a
Date
)

I managed to work around this issue and do this in the controller:

$scope.item = response;
$scope.item.startedAt = new Date($scope.item.startedAt);


It works this way... But I wouldn't like to manually convert string do date every time I get a response from the server. I tried to create a directive, that I can assign to the datepicker input field so it converts the
ng-model
for me:

.directive("asDate", function () {
return {
require: 'ngModel',
link: function (scope, element, attrs, modelCtrl) {

modelCtrl.$formatters.push(function (input) {

var transformedInput = new Date(input);

if (transformedInput != input) {
modelCtrl.$setViewValue(transformedInput);
modelCtrl.$render();
}

return transformedInput;
});
}
}
})


Well it works, because now I can see
Date
object, when I output model in my view:
{{item.startedAt}}
. However still validation fails! I suspect this is some problem with me understanding how data flows between model and the view, and how UI Bootstrap hooks into it.

Also when I change my directive from
$formatters.push
to
$formatters.unshift
, validation works OK, but datepicker does not format my datetime (insted of nicely formattet
yyyy-MM-dd
I see ISO string inside the input)

Answer

As this is intentional behaviour of angular-ui-bootstrap datepicker (https://github.com/angular-ui/bootstrap/issues/4690), I ended up using Angular service/factory and moment library.

Service dateConverter can be injected globally to intercept all HTTP requestes/responses or only in desired controllers.

Here I use Restangular library to handle request to REST API, hence the response.plain() method which takes only object properties, and not Restangular methods/properties.

var Services = angular.module('app.Services', []);

Services
    .factory('dateConverter', ['dateFilter', function (dateFilter) {

        var dateConverter = {};

        dateConverter.prepareResponse = function (response) {
            for(prop in response.plain()) {
                if (response.hasOwnProperty(prop)) {
                    if(moment(response[prop], moment.ISO_8601, true).isValid()) {
                        response[prop] = new Date(response[prop]);
                    }
                }
            }
            return response;
        };

        dateConverter.prepareRequest = function (item) {
            for(prop in item.plain()) {
                if (item.hasOwnProperty(prop)) {
                    if(angular.isDate(item[prop])){
                         item[prop] = dateFilter(item[prop] , "yyyy-MM-ddTHH:mm:ssZ")
                    }
                }
            }
            return item;
        };

        return dateConverter;
    }])
;
Comments