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

10,683

Solution 1

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;
    }])
;

Solution 2

you can transform String to Date in restangular transformer, something like this

RestangularConfigurer
  .addElementTransformer('<RESTRESOURCENAME>', false, function (element) {
      element.createDate = new Date(element.createDate);
      return element;
  })

Solution 3

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]);
};
Share:
10,683
rsobon
Author by

rsobon

Updated on July 26, 2022

Comments

  • rsobon
    rsobon almost 2 years

    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)

  • rsobon
    rsobon over 8 years
    when you are using javascript for-in loop you probable should also use hasOwnProperty()
  • Adeel Shekhani
    Adeel Shekhani over 4 years
    Any guidance how to use it?