Skip to content

Commit 30711a4

Browse files
committed
feat(datepicker): implements alternative format support
- Adds support for multiple input formats Closes angular-ui#4951 Closes angular-ui#4952
1 parent f3d4dc2 commit 30711a4

File tree

5 files changed

+100
-7
lines changed

5 files changed

+100
-7
lines changed

Diff for: src/datepicker/datepicker.js

+23-5
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
4141
angular.forEach(['minDate', 'maxDate'], function(key) {
4242
if ($attrs[key]) {
4343
$scope.$parent.$watch($attrs[key], function(value) {
44-
self[key] = value ? new Date(value) : null;
44+
self[key] = value ? angular.isDate(value) ? new Date(value) : new Date(dateFilter(value, 'medium')) : null;
4545
self.refreshView();
4646
});
4747
} else {
@@ -539,7 +539,8 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
539539
closeOnDateSelection: true,
540540
appendToBody: false,
541541
showButtonBar: true,
542-
onOpenFocus: true
542+
onOpenFocus: true,
543+
altInputFormats: []
543544
})
544545

545546
.controller('UibDatepickerPopupController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$document', '$rootScope', '$uibPosition', 'dateFilter', 'uibDateParser', 'uibDatepickerPopupConfig', '$timeout',
@@ -549,7 +550,7 @@ function(scope, element, attrs, $compile, $parse, $document, $rootScope, $positi
549550
isHtml5DateInput = false;
550551
var dateFormat, closeOnDateSelection, appendToBody, onOpenFocus,
551552
datepickerPopupTemplateUrl, datepickerTemplateUrl, popupEl, datepickerEl,
552-
ngModel, $popup;
553+
ngModel, $popup, altInputFormats;
553554

554555
scope.watchData = {};
555556

@@ -560,6 +561,7 @@ function(scope, element, attrs, $compile, $parse, $document, $rootScope, $positi
560561
onOpenFocus = angular.isDefined(attrs.onOpenFocus) ? scope.$parent.$eval(attrs.onOpenFocus) : datepickerPopupConfig.onOpenFocus;
561562
datepickerPopupTemplateUrl = angular.isDefined(attrs.datepickerPopupTemplateUrl) ? attrs.datepickerPopupTemplateUrl : datepickerPopupConfig.datepickerPopupTemplateUrl;
562563
datepickerTemplateUrl = angular.isDefined(attrs.datepickerTemplateUrl) ? attrs.datepickerTemplateUrl : datepickerPopupConfig.datepickerTemplateUrl;
564+
altInputFormats = angular.isDefined(attrs.altInputFormats) ? scope.$parent.$eval(attrs.altInputFormats) : datepickerPopupConfig.altInputFormats;
563565

564566
scope.showButtonBar = angular.isDefined(attrs.showButtonBar) ? scope.$parent.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar;
565567

@@ -626,10 +628,10 @@ function(scope, element, attrs, $compile, $parse, $document, $rootScope, $positi
626628
if (attrs[key]) {
627629
var getAttribute = $parse(attrs[key]);
628630
scope.$parent.$watch(getAttribute, function(value) {
629-
scope.watchData[key] = value;
630631
if (key === 'minDate' || key === 'maxDate') {
631-
cache[key] = new Date(value);
632+
cache[key] = angular.isDate(value) ? new Date(value) : new Date(dateFilter(value, 'medium'));
632633
}
634+
scope.watchData[key] = cache[key] || value;
633635
});
634636
datepickerEl.attr(cameltoDash(key), 'watchData.' + key);
635637

@@ -811,6 +813,14 @@ function(scope, element, attrs, $compile, $parse, $document, $rootScope, $positi
811813

812814
if (angular.isString(viewValue)) {
813815
var date = dateParser.parse(viewValue, dateFormat, scope.date);
816+
if (isNaN(date)) {
817+
for (var i = 0; i < altInputFormats.length; i++) {
818+
date = dateParser.parse(viewValue, altInputFormats[i], scope.date);
819+
if (!isNaN(date)) {
820+
break;
821+
}
822+
}
823+
}
814824
if (isNaN(date)) {
815825
return undefined;
816826
}
@@ -842,6 +852,14 @@ function(scope, element, attrs, $compile, $parse, $document, $rootScope, $positi
842852

843853
if (angular.isString(value)) {
844854
var date = dateParser.parse(value, dateFormat);
855+
if (isNaN(date)) {
856+
for (var i = 0; i < altInputFormats.length; i++) {
857+
date = dateParser.parse(value, altInputFormats[i]);
858+
if (!isNaN(date)) {
859+
break;
860+
}
861+
}
862+
}
845863
return !isNaN(date);
846864
}
847865

Diff for: src/datepicker/docs/demo.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ <h4>Popup</h4>
2222
<div class="row">
2323
<div class="col-md-6">
2424
<p class="input-group">
25-
<input type="text" class="form-control" uib-datepicker-popup="{{format}}" ng-model="dt" is-open="popup1.opened" min-date="minDate" max-date="maxDate" datepicker-options="dateOptions" date-disabled="disabled(date, mode)" ng-required="true" close-text="Close" />
25+
<input type="text" class="form-control" uib-datepicker-popup="{{format}}" ng-model="dt" is-open="popup1.opened" min-date="minDate" max-date="maxDate" datepicker-options="dateOptions" date-disabled="disabled(date, mode)" ng-required="true" close-text="Close" alt-input-formats="altInputFormats" />
2626
<span class="input-group-btn">
2727
<button type="button" class="btn btn-default" ng-click="open1()"><i class="glyphicon glyphicon-calendar"></i></button>
2828
</span>
@@ -40,7 +40,7 @@ <h4>Popup</h4>
4040
</div>
4141
<div class="row">
4242
<div class="col-md-6">
43-
<label>Format:</label> <select class="form-control" ng-model="format" ng-options="f for f in formats"><option></option></select>
43+
<label>Format: <span class="muted-text">(manual alternate <em>{{altInputFormats[0]}}</em>)</span></label> <select class="form-control" ng-model="format" ng-options="f for f in formats"><option></option></select>
4444
</div>
4545
</div>
4646

Diff for: src/datepicker/docs/demo.js

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ angular.module('ui.bootstrap.demo').controller('DatepickerDemoCtrl', function ($
3939

4040
$scope.formats = ['dd-MMMM-yyyy', 'yyyy/MM/dd', 'dd.MM.yyyy', 'shortDate'];
4141
$scope.format = $scope.formats[0];
42+
$scope.altInputFormats = ['M!/d!/yyyy'];
4243

4344
$scope.popup1 = {
4445
opened: false

Diff for: src/datepicker/docs/readme.md

+4
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,10 @@ Options for the uib-datepicker must be passed as JSON using the `datepicker-opti
144144
_(Default: `yyyy-MM-dd`)_ -
145145
The format for displayed dates. This string can take string literals by surrounding the value with single quotes, i.e. `yyyy-MM-dd h 'o\'clock'`.
146146

147+
* `alt-input-formats`
148+
_(Default: `[]`)_:
149+
A list of alternate formats acceptable for manual entry.
150+
147151
### Keyboard support ###
148152

149153
Depending on datepicker's current mode, the date may refer either to day, month or year. Accordingly, the term view refers either to a month, year or year range.

Diff for: src/datepicker/test/datepicker.spec.js

+70
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,20 @@ describe('datepicker', function() {
965965

966966
});
967967

968+
describe('`min-date` attribute', function () {
969+
beforeEach(function() {
970+
element = $compile('<uib-datepicker ng-model="date" min-date="\'2010-09-05\'"></uib-datepicker>')($rootScope);
971+
$rootScope.$digest();
972+
});
973+
974+
it('accepts literals, \'yyyy-MM-dd\' case', function() {
975+
var buttons = getAllOptionsEl();
976+
angular.forEach(buttons, function(button, index) {
977+
expect(angular.element(button).prop('disabled')).toBe(index < 7);
978+
});
979+
});
980+
});
981+
968982
describe('`max-date` attribute', function() {
969983
beforeEach(function() {
970984
$rootScope.maxdate = new Date('September 25, 2010');
@@ -1982,6 +1996,17 @@ describe('datepicker', function() {
19821996
expect(buttons.eq(0).prop('disabled')).toBe(true);
19831997
});
19841998

1999+
it('should disable today button if before min date, yyyy-MM-dd case', inject(function(dateFilter) {
2000+
var minDate = new Date(new Date().setDate(new Date().getDate() + 1));
2001+
var literalMinDate = dateFilter(minDate, 'yyyy-MM-dd');
2002+
var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup="yyyy-MM-dd" min-date="\'' + literalMinDate + '\'" is-open="true"><div>')($rootScope);
2003+
$rootScope.$digest();
2004+
assignElements(wrapElement);
2005+
assignButtonBar();
2006+
2007+
expect(buttons.eq(0).prop('disabled')).toBe(true);
2008+
}));
2009+
19852010
it('should disable today button if after max date', function() {
19862011
$rootScope.maxDate = new Date().setDate(new Date().getDate() - 2);
19872012
var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup max-date="maxDate" is-open="true"><div>')($rootScope);
@@ -2339,6 +2364,51 @@ describe('datepicker', function() {
23392364
});
23402365
});
23412366

2367+
describe('altInputFormats', function() {
2368+
describe('datepickerPopupConfig.altInputFormats', function() {
2369+
var originalConfig = {};
2370+
beforeEach(inject(function(uibDatepickerPopupConfig) {
2371+
angular.extend(originalConfig, uibDatepickerPopupConfig);
2372+
uibDatepickerPopupConfig.datepickerPopup = 'MM-dd-yyyy';
2373+
uibDatepickerPopupConfig.altInputFormats = ['M!/d!/yyyy'];
2374+
2375+
var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup is-open="true"></div>')($rootScope);
2376+
$rootScope.$digest();
2377+
assignElements(wrapElement);
2378+
}));
2379+
2380+
afterEach(inject(function(uibDatepickerPopupConfig) {
2381+
// return it to the original state
2382+
angular.extend(uibDatepickerPopupConfig, originalConfig);
2383+
}));
2384+
2385+
it('changes date format', function() {
2386+
changeInputValueTo(inputEl, '11/8/1980');
2387+
2388+
expect($rootScope.date.getFullYear()).toEqual(1980);
2389+
expect($rootScope.date.getMonth()).toEqual(10);
2390+
expect($rootScope.date.getDate()).toEqual(8);
2391+
});
2392+
});
2393+
2394+
describe('attribute `alt-input-formats`', function() {
2395+
beforeEach(function() {
2396+
$rootScope.date = new Date('November 9, 1980');
2397+
var wrapElement = $compile('<div><input ng-model="date" uib-datepicker-popup="MMMM d yyyy" alt-input-formats="[\'M!/d!/yyyy\']" is-open="true"></div>')($rootScope);
2398+
$rootScope.$digest();
2399+
assignElements(wrapElement);
2400+
});
2401+
2402+
it('should accept alternate input formats', function() {
2403+
changeInputValueTo(inputEl, '11/8/1980');
2404+
2405+
expect($rootScope.date.getFullYear()).toEqual(1980);
2406+
expect($rootScope.date.getMonth()).toEqual(10);
2407+
expect($rootScope.date.getDate()).toEqual(8);
2408+
});
2409+
});
2410+
});
2411+
23422412
describe('pass through attributes', function() {
23432413
var wrapElement;
23442414
describe('formatting', function() {

0 commit comments

Comments
 (0)