Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)

like image 279
rsobon Avatar asked Nov 10 '15 09:11

rsobon


Video Answer


1 Answers

This broke as of Angular.UI.Bootstrap v0.13.2 (8-2-2015) Downgrading to 0.13.1 works, which is where I'm stuck today.

Wesleycho says this was done intentionally https://github.com/angular-ui/bootstrap/issues/4690

I'm ready for other date pickers that support strings if anyone has a suggestion

...soon after posting this I went down a non-angular path that I'm not proud of, but it works for both HTML5 type="date" and uib-datepicker-popup. I have a regular expression that determines if a string resembles one of the two serialized date formats I've seen, and then I have a recursive javascript function to traverse a json tree and replace those strings with Date(). You would call it just before you put in $scope (or viewmodel) ...

$http.get("../api/comm/" + commId
    ).success(function (resp) {
        fixDates(resp);
        vm.comm = resp;
    });

(I need not check the string length, but I figured it would spare some cpu cycles by not running the regex if the string is obviously not a date)

//2015-10-01T00:00:00-04:00
//2015-11-20T18:15:56.6229516-05:00
var isDate = new RegExp("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{7})?-\\d{2}:00");

function fixDates(json) {
    for (i in json)
        if (typeof (json[i]) == "object")
            fixDates(json[i]);
        else if (typeof (json[i]) == "string" && (json[i].length == 25 || json[i].length == 33) && isDate.test(json[i]))
            json[i] = new Date(json[i]);
};
like image 183
Victor Wynnytsky Avatar answered Sep 22 '22 18:09

Victor Wynnytsky