Skip to content

Commit c93ba3f

Browse files
committed
feat(datepicker): timeless-json-mode
datepickerConfig option for json models when we don't care about the time. * on init and parse: sets model to 'yyyy-MM-dd' format * on format: converts model to date before normal formatting [codepen](http://codepen.io/davious/pen/gpyVQm?editors=101) Related: angular-ui#1891, angular-ui#2702, angular-ui#2906, angular-ui#2952
1 parent 5bc0851 commit c93ba3f

File tree

2 files changed

+124
-2
lines changed

2 files changed

+124
-2
lines changed

Diff for: src/datepicker/datepicker.js

+38-2
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,7 @@ function($compile, $parse, $document, $rootScope, $position, dateFilter, datePar
512512
onOpenFocus = angular.isDefined(attrs.onOpenFocus) ? scope.$parent.$eval(attrs.onOpenFocus) : datepickerPopupConfig.onOpenFocus,
513513
datepickerPopupTemplateUrl = angular.isDefined(attrs.datepickerPopupTemplateUrl) ? attrs.datepickerPopupTemplateUrl : datepickerPopupConfig.datepickerPopupTemplateUrl,
514514
datepickerTemplateUrl = angular.isDefined(attrs.datepickerTemplateUrl) ? attrs.datepickerTemplateUrl : datepickerPopupConfig.datepickerTemplateUrl,
515+
timelessJsonMode = angular.isDefined(attrs.timelessJsonMode) ? attrs.timelessJsonMode : datepickerPopupConfig.timelessJsonMode,
515516
cache = {};
516517

517518
scope.showButtonBar = angular.isDefined(attrs.showButtonBar) ? scope.$parent.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar;
@@ -533,6 +534,19 @@ function($compile, $parse, $document, $rootScope, $position, dateFilter, datePar
533534
return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()));
534535
};
535536

537+
scope.jsonToDate = function(date) {
538+
if (date && angular.isString(date) && date.indexOf('-') > -1) {
539+
var update = date.indexOf('T') > -1;
540+
date = date.substring(0, 10).split('-');
541+
date = new Date(+date[0], +date[1] - 1, +date[2]);
542+
if(update) {
543+
ngModel.$setViewValue(dateFilter(date, dateFormat));
544+
ngModel.$render();
545+
}
546+
}
547+
return date;
548+
};
549+
536550
var isHtml5DateInput = false;
537551
if (datepickerPopupConfig.html5Types[attrs.type]) {
538552
dateFormat = datepickerPopupConfig.html5Types[attrs.type];
@@ -647,15 +661,15 @@ function($compile, $parse, $document, $rootScope, $position, dateFilter, datePar
647661
if (isNaN(date)) {
648662
return undefined;
649663
} else {
650-
return date;
664+
return timelessJsonMode ? dateFilter(date, 'yyyy-MM-dd') : date;
651665
}
652666
} else {
653667
return undefined;
654668
}
655669
}
656670

657671
function validator(modelValue, viewValue) {
658-
var value = modelValue || viewValue;
672+
var value = (timelessJsonMode ? scope.jsonToDate(modelValue) : modelValue) || viewValue;
659673

660674
if (!attrs.ngRequired && !value) {
661675
return true;
@@ -682,11 +696,17 @@ function($compile, $parse, $document, $rootScope, $position, dateFilter, datePar
682696
ngModel.$validators.date = validator;
683697
ngModel.$parsers.unshift(parseDate);
684698
ngModel.$formatters.push(function(value) {
699+
if(timelessJsonMode) {
700+
value = scope.jsonToDate(value);
701+
}
685702
scope.date = value;
686703
return ngModel.$isEmpty(value) ? value : dateFilter(value, dateFormat);
687704
});
688705
} else {
689706
ngModel.$formatters.push(function(value) {
707+
if(timelessJsonMode) {
708+
value = scope.jsonToDate(value);
709+
}
690710
scope.date = value;
691711
return value;
692712
});
@@ -820,4 +840,20 @@ function($compile, $parse, $document, $rootScope, $position, dateFilter, datePar
820840
return attrs.templateUrl || 'template/datepicker/popup.html';
821841
}
822842
};
843+
})
844+
845+
.directive('datepickerJsonUtils', function() {
846+
return {
847+
restrict: 'A',
848+
link: function(scope) {
849+
scope.jsonToTicks = function(date) {
850+
if (date && angular.isString(date) && date.indexOf('-') > -1) {
851+
date = date.substring(0, 10).split('-');
852+
date = new Date(+date[0], +date[1] - 1, +date[2]);
853+
}
854+
return date.getTime ? date.getTime() : null;
855+
};
856+
scope.oneDay = 1000 * 60 * 60 * 24;
857+
}
858+
};
823859
});

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

+86
Original file line numberDiff line numberDiff line change
@@ -1255,6 +1255,92 @@ describe('datepicker directive', function() {
12551255

12561256
});
12571257

1258+
describe('datepickerPopupConfig.timelessJsonMode = true', function() {
1259+
var inputEl, dropdownEl, $document, $sniffer, $timeout;
1260+
1261+
function assignElements(wrapElement) {
1262+
inputEl = wrapElement.find('input');
1263+
dropdownEl = wrapElement.find('ul');
1264+
element = dropdownEl.find('table');
1265+
}
1266+
1267+
beforeEach(inject(function(datepickerPopupConfig) {
1268+
datepickerPopupConfig.timelessJsonMode = true;
1269+
$rootScope.date = '2010-09-30T00:00:00.000';
1270+
$rootScope.isopen = true;
1271+
var wrapper = $compile('<div><input ng-model="date" datepicker-popup="MM/dd/yyyy" is-open="isopen"></div>')($rootScope);
1272+
$rootScope.$digest();
1273+
assignElements(wrapper);
1274+
}));
1275+
1276+
afterEach(inject(function (datepickerPopupConfig) {
1277+
// return it to the original state
1278+
delete datepickerPopupConfig.timelessJsonMode;
1279+
}));
1280+
1281+
it('formats model to yyyy-MM-dd on initialization', function() {
1282+
expect(inputEl.val()).toBe('09/30/2010');
1283+
expect($rootScope.date).toEqual('2010-09-30');
1284+
});
1285+
1286+
it('updates the input when a day is clicked', function() {
1287+
clickOption(17);
1288+
expect(inputEl.val()).toBe('09/15/2010');
1289+
expect($rootScope.date).toEqual('2010-09-15');
1290+
});
1291+
});
1292+
1293+
describe('timeless-json-mode="true"', function() {
1294+
var inputEl, dropdownEl, $document, $sniffer, $timeout;
1295+
1296+
function assignElements(wrapElement) {
1297+
inputEl = wrapElement.find('input');
1298+
dropdownEl = wrapElement.find('ul');
1299+
element = dropdownEl.find('table');
1300+
}
1301+
1302+
beforeEach(inject(function() {
1303+
$rootScope.date = '2010-09-30T00:00:00.000';
1304+
$rootScope.isopen = true;
1305+
var wrapper = $compile('<div><input ng-model="date" datepicker-popup="MM/dd/yyyy" timeless-json-mode="true" is-open="isopen"></div>')($rootScope);
1306+
$rootScope.$digest();
1307+
assignElements(wrapper);
1308+
}));
1309+
1310+
it('formats model to yyyy-MM-dd on initialization', function() {
1311+
expect(inputEl.val()).toBe('09/30/2010');
1312+
expect($rootScope.date).toEqual('2010-09-30');
1313+
});
1314+
1315+
it('updates the input when a day is clicked', function() {
1316+
clickOption(17);
1317+
expect(inputEl.val()).toBe('09/15/2010');
1318+
expect($rootScope.date).toEqual('2010-09-15');
1319+
});
1320+
});
1321+
1322+
describe('datepickerJsonUtils', function() {
1323+
var inputEl, dropdownEl, $document, $sniffer, $timeout;
1324+
1325+
function assignElements(wrapElement) {
1326+
inputEl = wrapElement.find('input');
1327+
dropdownEl = wrapElement.find('ul');
1328+
element = dropdownEl.find('table');
1329+
}
1330+
1331+
beforeEach(inject(function() {
1332+
$rootScope.date = '2010-09-15T00:00:00.000';
1333+
$rootScope.isopen = true;
1334+
var wrapper = $compile('<div><input ng-model="date" datepicker-popup="MM/dd/yyyy" timeless-json-mode="true" datepicker-json-utils max-date="jsonToTicks(date) + oneDay" is-open="isopen"></div>')($rootScope);
1335+
$rootScope.$digest();
1336+
assignElements(wrapper);
1337+
}));
1338+
1339+
it('jsonToDate should be on scope and work with max-date', function() {
1340+
expect(getAllOptionsEl().eq(19).prop('disabled')).toBe(true);
1341+
});
1342+
});
1343+
12581344
describe('setting datepickerPopupConfig inside ng-if', function() {
12591345
var originalConfig = {};
12601346
beforeEach(inject(function (datepickerPopupConfig) {

0 commit comments

Comments
 (0)