Reputation: 133
I am getting a date time value from asp.net mvc controller as "2014-08-31T00:00:00Z". When I bind this value to my angular-ui datepicker control it's state is showing as ng-invalid ng-invalid-date.
I am getting the date-format as well from the mvc controller so I am binding the date-format as well in my html.
When I am debugging the ui-bootstrap-tpls.js (latest version) file at line 1807
It's always coming as undefined. I have tried so many alternatives but I am unable to succeed. :(
javascript does't convert angular ui datepicker date to UTC correctly
So please give some thoughts and suggest me how can I solve this problem.
Thanks & Regards, N.Murali Krishna.
Upvotes: 7
Views: 18652
Reputation: 1550
I solved the problem by converting string to valid javascript formt in directive
:
see here: Datepicker-popup formatting not working when value set initially in scope
Upvotes: 1
Reputation: 11
Just wanted to add my solution to this stack.
app.directive('formatDate', function (dateFilter, uibDateParser, uibDatepickerPopupConfig) {
return {
restrict: 'A',
priority: 1,
require: 'ngModel',
link: function (scope, element, attr, ngModel) {
var dateFormat = attr.uibDatepickerPopup || uibDatepickerPopupConfig.datepickerPopup;
ngModel.$validators.date = function(modelValue, viewValue) {
var value = viewValue || modelValue;
if (!attr.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 = new Date(value);
return !isNaN(date);
} else {
return false;
}
}
ngModel.$formatters.push(function(value) {
return new Date(value);
});
}
};
});
Upvotes: 1
Reputation: 491
To use formatted date string instead of Date object as ng-model for Angular Datepicker, you need to create a wrapper directive. The wrapper directive will parse your string into Date object and pass it to the datepicker. When you select a date, it is converted from Date back into string. 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)"> <i class="glyphicon glyphicon-calendar"></i> </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>
Upvotes: 2
Reputation: 61
This is best solution i found so far:
.directive('uibDatepickerPopup', function (dateFilter, uibDateParser, uibDatepickerPopupConfig) {
return {
restrict: 'A',
priority: 1,
require: 'ngModel',
link: function (scope, element, attr, ngModel) {
var dateFormat = attr.uibDatepickerPopup || uibDatepickerPopupConfig.datepickerPopup;
ngModel.$validators.date = function(modelValue, viewValue) {
var value = viewValue || modelValue;
if (!attr.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 = uibDateParser.parse(value, dateFormat);
return !isNaN(date);
} else {
return false;
}
};
}
};
});
Upvotes: 6
Reputation: 4643
A couple of notes here:
datepicker
directive requires that the ng-model
be a Date
object. This is documented here.Date
object if it matches a hard-coded pattern. This is not flexible nor is it easily testable. It will not be the right solution for most people.If, in your system, global date string conversion is the right behavior, the proper Angular design would be to create a service that does this for you. This leads me to...
We've (Angular UI Bootstrap) provided a mechanism for converting date strings into Date
objects via the dateParser
service. You can see the source code here. NB: this service name becomes deprecated and changed to uibDateParser
with the 0.14.0 release.
Upvotes: 6
Reputation: 21
I don't have enough reputation to comment under Chet's answer but thanks so much this solution worked for me as well! I don't know your Github handle to tag you in the issue but I submitted a Github issue under Angular UI to help others find the solution and hopefully get Angular UI guys to look into it. Thanks again!
https://github.com/angular-ui/bootstrap/issues/4554
Upvotes: 2
Reputation: 3729
I had the same problem. The issue is that Angular is expecting an actual date object, not a string representation of the date. After doing a bunch of research I ended up adding a transformReponse to the $httpProvider which checks all string objects to see if they can be converted to a date, and if so actually converting them.
angular.module('test')
.config(['$httpProvider', function ($httpProvider) {
// ISO 8601 Date Pattern: YYYY-mm-ddThh:MM:ss
var dateMatchPattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/;
var convertDates = function (obj) {
for (var key in obj) {
if (!obj.hasOwnProperty(key)) continue;
var value = obj[key];
var typeofValue = typeof (value);
if (typeofValue === 'object') {
// If it is an object, check within the object for dates.
convertDates(value);
} else if (typeofValue === 'string') {
if (dateMatchPattern.test(value)) {
obj[key] = new Date(value);
}
}
}
}
$httpProvider.defaults.transformResponse.push(function (data) {
if (typeof (data) === 'object') {
convertDates(data);
}
return data;
});
}])
Upvotes: 11