63

I am using bootstrap date-picker in my angular application. However when I select a date from that date-picker underlying ng-model that I have bind gets updated I want that ng-model in one date format 'MM/dd/yyyy'. but it every times makes date like this

"2009-02-03T18:30:00.000Z"

instead of

02/04/2009

I have created a plunkr for the same plunkr link

My Html and controller code is like below

<!doctype html>
<html ng-app="plunker">
  <head>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.js"></script>
    <script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.11.0.js"></script>
    <script src="example.js"></script>
    <link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
  </head>
  <body>

<div ng-controller="DatepickerDemoCtrl">
    <pre>Selected date is: <em>{{dt | date:'MM/dd/yyyy' }}</em></pre>
    <p>above filter will just update above UI but I want to update actual ng-modle</p>
    

    <h4>Popup</h4>
    <div class="row">
        <div class="col-md-6">
            <p class="input-group">
              <input type="text" class="form-control"
              datepicker-popup="{{format}}" 
              ng-model="dt"
              is-open="opened" min-date="minDate"
              max-date="'2015-06-22'" 
              datepicker-options="dateOptions" 
              date-disabled="disabled(date, mode)" 
              ng-required="true" close-text="Close" />
              <span class="input-group-btn"> 
                <button type="button" class="btn btn-default" ng-click="open($event)">
                <i class="glyphicon glyphicon-calendar"></i></button>
              </span>
            </p>
        </div>
    </div>
    <!--<div class="row">
        <div class="col-md-6">
            <label>Format:</label> <select class="form-control" ng-model="format" ng-options="f for f in formats"><option></option></select>
        </div>
    </div>-->

    <hr />
    {{dt}}
</div>
  </body>
</html>

Angular controller

angular.module('plunker', ['ui.bootstrap']);
var DatepickerDemoCtrl = function ($scope) {


  $scope.open = function($event) {
    $event.preventDefault();
    $event.stopPropagation();

    $scope.opened = true;
  };

  $scope.dateOptions = {
    formatYear: 'yy',
    startingDay: 1
  };

 
  $scope.format = 'dd-MMMM-yyyy';
};

UPDATE

I am calling below method for posting my data and VAR is array of size 900 which contains date-picker variables.

public SaveCurrentData(formToSave: tsmodels.ResponseTransferCalculationModelTS) {

        var query = this.EntityQuery.from('SaveFormData').withParameters({
            $method: 'POST',
            $encoding: 'JSON',
            $data: {
                VAR: formToSave.VAR,
                X: formToSave.X,
                CurrentForm: formToSave.currentForm,
            }
        });

        var deferred = this.q.defer();

        this.manager.executeQuery(query).then((response) => {
            deferred.resolve(response);
        }, (error) => {
                deferred.reject(error);
            });

        return deferred.promise;
    }
starball
  • 20,030
  • 7
  • 43
  • 238
Jenish Rabadiya
  • 6,708
  • 6
  • 33
  • 62

13 Answers13

105

Although similar answers have been posted I'd like to contribute what seemed to be the easiest and cleanest fix to me. Assuming you are using the AngularUI datepicker and your initial value for the ng-Model does not get formatted simply adding the following directive to your project will fix the issue:

angular.module('yourAppName')
.directive('datepickerPopup', function (){
    return {
        restrict: 'EAC',
        require: 'ngModel',
        link: function(scope, element, attr, controller) {
      //remove the default formatter from the input directive to prevent conflict
      controller.$formatters.shift();
  }
}
});

I found this solution in the Github AngularUI issues and therefore all credit goes to the people over there.

stefan2k
  • 1,137
  • 1
  • 9
  • 14
  • 6
    This needs to be higher voted than the accepted answer, especially if using bower or a pkg manager to get angular-ui. Worked perfectly, and I can leave angular-ui.js alone. – notbrain Feb 13 '15 at 01:24
  • I concur - this answer is far better, especially when one is using the pre-minified code from the nuget package. Manual edits mean that you have to re-create them every time you update the code, while this is permanent and simple - and if this bug gets fixed, you just delete the directive. This is by far the better answer. – cidthecoatrack Feb 21 '15 at 20:37
  • I've been playing around with my sql date types, formatting, etc. for a couple of days to make this thing work. This answer immediately fixed it. argggg! lol time I'll never get back... – jenki221 May 29 '15 at 13:42
  • Worked for me. Thank you very much! – John Jun 19 '15 at 08:34
  • 3
    Did not work for me on angular 1.4 with angular-bootstrap 0.13.3. [this answer](http://stackoverflow.com/a/28202178/2614353) did! – Tom Bevelander Sep 04 '15 at 10:02
  • Worked for me with Angular 1.6.5 and bootstrap 0.12. Thanks for bringing this to SO. Very elegant solution. – Kevin Jul 20 '17 at 16:29
24

You can make use of $parsers as shown below,this solved it for me.

window.module.directive('myDate', function(dateFilter) {
  return {
    restrict: 'EAC',
    require: '?ngModel',
    link: function(scope, element, attrs, ngModel) {
      ngModel.$parsers.push(function(viewValue) {
        return dateFilter(viewValue,'yyyy-MM-dd');
      });
    }
  };
});

HTML:

<p class="input-group datepicker" >
  <input
     type="text"
     class="form-control"
     name="name"
     datepicker-popup="yyyy-MM-dd"
     date-type="string"
     show-weeks="false"
     ng-model="data[$parent.editable.name]" 
     is-open="$parent.opened"
     min-date="minDate"
     close-text="Close"
     ng-required="{{editable.mandatory}}"
     show-button-bar="false"
     close-on-date-selection="false"
     my-date />
  <span class="input-group-btn">
    <button type="button" class="btn btn-default" ng-click="openDatePicker($event)">
      <i class="glyphicon glyphicon-calendar"></i>
    </button>
  </span>
</p>
Janne Annala
  • 25,928
  • 8
  • 31
  • 41
Rishab777
  • 565
  • 6
  • 14
13

I ran into the same problem and after a couple of hours of logging and investigating, I fixed it.

It turned out that for the first time the value is set in a date picker, $viewValue is a string so the dateFilter displays it as is. All I did is parse it into a Date object.

Search for that block in ui-bootstrap-tpls file

  ngModel.$render = function() {
    var date = ngModel.$viewValue ? dateFilter(ngModel.$viewValue, dateFormat) : '';
    element.val(date);

    updateCalendar();
  };

and replace it by:

  ngModel.$render = function() {
    ngModel.$viewValue = new Date(ngModel.$viewValue);
    var date = ngModel.$viewValue ? dateFilter(ngModel.$viewValue, dateFormat) : '';
    element.val(date);

    updateCalendar();
  };

Hopefully this will help :)

Christina
  • 1,136
  • 11
  • 16
  • 1
    if (!ngModel.$viewValue)ngModel.$viewValue = new Date(); else ngModel.$viewValue = new Date(ngModel.$viewValue); – Christina Oct 20 '14 at 07:59
  • Slightly better fix in https://github.com/angular-ui/bootstrap/pull/2943, which handles the case when the value is `null`. – Kris Braun Dec 30 '14 at 03:49
  • 6
    This does not work if you are using angular-bootstrap from bower or another package manager. Editing the library itself is not recommended. – notbrain Feb 13 '15 at 01:25
9

The format specified through datepicker-popup is just the format for the displayed date. The underlying ngModel is a Date object. Trying to display it will show it as it's default, standard-compliant rapresentation.

You can show it as you want by using the date filter in the view, or, if you need it to be parsed in the controller, you can inject $filter in your controller and call it as $filter('date')(date, format). See also the date filter docs.

link
  • 1,676
  • 1
  • 13
  • 21
  • yeh I got your point. But I am generating date-pickers dynamically on screen so don't have place to parse that ng-model. – Jenish Rabadiya Jun 13 '14 at 13:16
  • Ok, but where do you want to use the `ngModel` from the datepicker? If it's for display in the UI just use `date`. If it's in a service/controller just use `$filter('date')`. – link Jun 13 '14 at 13:44
  • Actually my ngModel has long array which is being posted to REST service. – Jenish Rabadiya Jun 13 '14 at 13:50
  • Now I think, I should go with making my own directive which will contain datepicker-popup in template and will parse the underlying date object into the desired format. – Jenish Rabadiya Jun 13 '14 at 13:53
  • But you will end up using a filter or formatter in your own directive too, I can't see how you would avoid that. Could you prepare a fiddle that shows how are you using the date from the datepicker? – link Jun 13 '14 at 13:57
  • I finally got the solution. Look at my answer http://stackoverflow.com/questions/24198669/angular-bootsrap-datepicker-date-format-does-not-format-ng-model-value/24282850#24282850 – Jenish Rabadiya Jun 18 '14 at 10:13
5

You may use formatters after picking value inside your datepicker directive. For example

angular.module('foo').directive('bar', function() {
    return {
        require: '?ngModel',
        link: function(scope, elem, attrs, ctrl) {
            if (!ctrl) return;

            ctrl.$formatters.push(function(value) {
                if (value) {
                    // format and return date here
                }

                return undefined;
            });
        }
    };
});

LINK.

Miraage
  • 3,334
  • 3
  • 26
  • 43
  • yeh that's what I am saying in link's conversion in above answer. but I need to use parser to update underlying ng-model and If I would implement parser that update ng-model than it might invoke date-picker directive again to update date. I am not sure about this but I will try this out. – Jenish Rabadiya Jun 16 '14 at 11:03
  • I finally got the solution. Look at my answer http://stackoverflow.com/questions/24198669/angular-bootsrap-datepicker-date-format-does-not-format-ng-model-value/24282850#24282850 – Jenish Rabadiya Jun 18 '14 at 10:13
  • You're on the right track but I believe it's supposed to be ctrl.$parsers.push since he wants his ng-model to store the modified value. – mikelt21 Dec 15 '14 at 21:51
4

With so many answers already written, Here's my take.

With Angular 1.5.6 & ui-bootstrap 1.3.3 , just add this on the model & you are done.

ng-model-options="{timezone: 'UTC'}" 

Note: Use this only if you are concerned about the date being 1 day behind & not bothered with extra time of T00:00:00.000Z

Updated Plunkr Here :

http://plnkr.co/edit/nncmB5EHEUkZJXRwz5QI?p=preview

Abdul Rehman Sayed
  • 6,532
  • 7
  • 45
  • 74
3

All proposed solutions didn't work for me but the closest one was from @Rishii.

I'm using AngularJS 1.4.4 and UI Bootstrap 0.13.3.

.directive('jsr310Compatible', ['dateFilter', 'dateParser', function(dateFilter, dateParser) {
  return {
    restrict: 'EAC',
    require: 'ngModel',
    priority: 1,
    link: function(scope, element, attrs, ngModel) {
      var dateFormat = 'yyyy-MM-dd';

      ngModel.$parsers.push(function(viewValue) {
        return dateFilter(viewValue, dateFormat);
      });

      ngModel.$validators.date = function (modelValue, viewValue) {
        var value = modelValue || viewValue;

        if (!attrs.ngRequired && !value) {
          return true;
        }

        if (angular.isNumber(value)) {
          value = new Date(value);
        }

        if (!value) {
          return true;
        }
        else if (angular.isDate(value) && !isNaN(value)) {
          return true;
        }
        else if (angular.isString(value)) {
          var date = dateParser.parse(value, dateFormat);
          return !isNaN(date);
        }
        else {
          return false;
        }
      };
    }
  };
}])
Ludovic Guillaume
  • 3,237
  • 1
  • 24
  • 41
1

I can fix this by adding below code in my JSP file. Now both model and UI values are same.

<div ng-show="false">
    {{dt = (dt | date:'dd-MMMM-yyyy') }}
</div>  
  • It seems to me that your problem is quite different than whatever has been mentioned above(question). Above question states having issue whlie backend model needs some other type than date and what you want seems to show something different on the UI. Remember with your solution backend model value will be the same date object. – Jenish Rabadiya Nov 02 '15 at 12:59
1

Steps to change the default date format of ng-model

For different date formats check the jqueryui datepicker date format values here for example I have used dd/mm/yy

Create angularjs directive

angular.module('app', ['ui.bootstrap']).directive('dt', function () {
return {
    restrict: 'EAC',
    require: 'ngModel',
    link: function (scope, element, attr, ngModel) {
        ngModel.$parsers.push(function (viewValue) {
           return dateFilter(viewValue, 'dd/mm/yy');
        });
    }
 }
});

Write dateFilter function

function dateFilter(val,format) {
    return $.datepicker.formatDate(format,val);
}

In html page write the ng-modal attribute

<input type="text" class="form-control" date-type="string"  uib-datepicker-popup="{{format}}" ng-model="src.pTO_DATE" is-open="popup2.opened" datepicker-options="dateOptions" ng-required="true" close-text="Close" show-button-bar="false" show-weeks="false" dt />
Sumit Jambhale
  • 565
  • 8
  • 13
1

The datepicker (and datepicker-popup) directive requires that the ng-model be a Date object. This is documented here.

If you want ng-model to be a string in specific format, you should create a wrapper directive. Here is an example (Plunker):

(function () {
    'use strict';

    angular
        .module('myExample', ['ngAnimate', 'ngSanitize', 'ui.bootstrap'])
        .controller('MyController', MyController)
        .directive('myDatepicker', myDatepickerDirective);

    MyController.$inject = ['$scope'];

    function MyController ($scope) {
      $scope.dateFormat = 'dd MMMM yyyy';
      $scope.myDate = '30 Jun 2017';
    }

    myDatepickerDirective.$inject = ['uibDateParser', '$filter'];

    function myDatepickerDirective (uibDateParser, $filter) {
        return {
            restrict: 'E',
            scope: {
                name: '@',
                dateFormat: '@',
                ngModel: '='
            },
            required: 'ngModel',
            link: function (scope) {

                var isString = angular.isString(scope.ngModel) && scope.dateFormat;

                if (isString) {
                    scope.internalModel = uibDateParser.parse(scope.ngModel, scope.dateFormat);
                } else {
                    scope.internalModel = scope.ngModel;
                }

                scope.open = function (event) {
                    event.preventDefault();
                    event.stopPropagation();
                    scope.isOpen = true;
                };

                scope.change = function () {
                    if (isString) {
                        scope.ngModel = $filter('date')(scope.internalModel, scope.dateFormat);
                    } else {
                        scope.ngModel = scope.internalModel;
                    }
                };

            },
            template: [
                '<div class="input-group">',
                    '<input type="text" readonly="true" style="background:#fff" name="{{name}}" class="form-control" uib-datepicker-popup="{{dateFormat}}" ng-model="internalModel" is-open="isOpen" ng-click="open($event)" ng-change="change()">',
                    '<span class="input-group-btn">',
                        '<button class="btn btn-default" ng-click="open($event)">&nbsp;<i class="glyphicon glyphicon-calendar"></i>&nbsp;</button>',
                    '</span>',
                '</div>'
            ].join('')
        }
    }

})();
<!DOCTYPE html>
<html>

  <head>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular-animate.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular-sanitize.js"></script>
    <script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-2.5.0.js"></script>
    <script src="example.js"></script>
    <link href="//netdna.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
  </head>

  <body ng-app="myExample">
    <div ng-controller="MyController">
      <p>
        Date format: {{dateFormat}}
      </p>
      <p>
        Value: {{myDate}}
      </p>
      <p>
        <my-datepicker ng-model="myDate" date-format="{{dateFormat}}"></my-datepicker>
      </p>
    </div>
  </body>

</html>
user147677
  • 471
  • 4
  • 7
0

Finally I got work around to the above problem. angular-strap has exactly the same feature that I am expecting. Just by applying date-format="MM/dd/yyyy" date-type="string" I got my expected behavior of updating ng-model in given format.

<div class="bs-example" style="padding-bottom: 24px;" append-source>
    <form name="datepickerForm" class="form-inline" role="form">
      <!-- Basic example -->
      <div class="form-group" ng-class="{'has-error': datepickerForm.date.$invalid}">
        <label class="control-label"><i class="fa fa-calendar"></i> Date <small>(as date)</small></label>
        <input type="text"  autoclose="true"  class="form-control" ng-model="selectedDate" name="date" date-format="MM/dd/yyyy" date-type="string" bs-datepicker>
      </div>
      <hr>
      {{selectedDate}}
     </form>
</div>

here is working plunk link

Jenish Rabadiya
  • 6,708
  • 6
  • 33
  • 62
0

Defining a new directive to work around a bug is not really ideal.

Because the datepicker displays later dates correctly, one simple workaround could be just setting the model variable to null first, and then to the current date after a while:

$scope.dt = null;
$timeout( function(){
    $scope.dt = new Date();
},100);
gm2008
  • 4,245
  • 1
  • 36
  • 38
0

After checking the above answers, I came up with this and it worked perfectly without having to add an extra attribute to your markup

angular.module('app').directive('datepickerPopup', function(dateFilter) {
    return {
        restrict: 'EAC',
        require: 'ngModel',
        link: function(scope, element, attr, ngModel) {
            ngModel.$parsers.push(function(viewValue) {
                return dateFilter(viewValue, 'yyyy-MM-dd');
            });
        }
    }
});