Skip to content
This repository was archived by the owner on May 29, 2019. It is now read-only.

Commit e04b06d

Browse files
committed
feat(datepicker): allow custom templates
- Allow custom templates for datepicker and popup - Expose controller to template Closes #4157 Resolves #1913
1 parent 68afc4c commit e04b06d

File tree

3 files changed

+105
-13
lines changed

3 files changed

+105
-13
lines changed

src/datepicker/datepicker.js

+18-8
Original file line numberDiff line numberDiff line change
@@ -225,21 +225,22 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
225225
return {
226226
restrict: 'EA',
227227
replace: true,
228-
templateUrl: 'template/datepicker/datepicker.html',
228+
templateUrl: function(element, attrs) {
229+
return attrs.templateUrl || 'template/datepicker/datepicker.html';
230+
},
229231
scope: {
230232
datepickerMode: '=?',
231233
dateDisabled: '&',
232234
customClass: '&',
233235
shortcutPropagation: '&?'
234236
},
235-
require: ['datepicker', '?^ngModel'],
237+
require: ['datepicker', '^ngModel'],
236238
controller: 'DatepickerController',
239+
controllerAs: 'datepicker',
237240
link: function(scope, element, attrs, ctrls) {
238241
var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
239242

240-
if ( ngModelCtrl ) {
241-
datepickerCtrl.init( ngModelCtrl );
242-
}
243+
datepickerCtrl.init(ngModelCtrl);
243244
}
244245
};
245246
})
@@ -477,6 +478,8 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
477478

478479
.constant('datepickerPopupConfig', {
479480
datepickerPopup: 'yyyy-MM-dd',
481+
datepickerPopupTemplateUrl: 'template/datepicker/popup.html',
482+
datepickerTemplateUrl: 'template/datepicker/datepicker.html',
480483
html5Types: {
481484
date: 'yyyy-MM-dd',
482485
'datetime-local': 'yyyy-MM-ddTHH:mm:ss.sss',
@@ -508,7 +511,9 @@ function ($compile, $parse, $document, $rootScope, $position, dateFilter, datePa
508511
var dateFormat,
509512
closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$parent.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection,
510513
appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? scope.$parent.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody,
511-
onOpenFocus = angular.isDefined(attrs.onOpenFocus) ? scope.$parent.$eval(attrs.onOpenFocus) : datepickerPopupConfig.onOpenFocus;
514+
onOpenFocus = angular.isDefined(attrs.onOpenFocus) ? scope.$parent.$eval(attrs.onOpenFocus) : datepickerPopupConfig.onOpenFocus,
515+
datepickerPopupTemplateUrl = angular.isDefined(attrs.datepickerPopupTemplateUrl) ? attrs.datepickerPopupTemplateUrl : datepickerPopupConfig.datepickerPopupTemplateUrl,
516+
datepickerTemplateUrl = angular.isDefined(attrs.datepickerTemplateUrl) ? attrs.datepickerTemplateUrl : datepickerPopupConfig.datepickerTemplateUrl;
512517

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

@@ -549,7 +554,8 @@ function ($compile, $parse, $document, $rootScope, $position, dateFilter, datePa
549554
var popupEl = angular.element('<div datepicker-popup-wrap><div datepicker></div></div>');
550555
popupEl.attr({
551556
'ng-model': 'date',
552-
'ng-change': 'dateSelection(date)'
557+
'ng-change': 'dateSelection(date)',
558+
'template-url': datepickerPopupTemplateUrl
553559
});
554560

555561
function cameltoDash( string ){
@@ -558,6 +564,8 @@ function ($compile, $parse, $document, $rootScope, $position, dateFilter, datePa
558564

559565
// datepicker element
560566
var datepickerEl = angular.element(popupEl.children()[0]);
567+
datepickerEl.attr('template-url', datepickerTemplateUrl);
568+
561569
if (isHtml5DateInput) {
562570
if (attrs.type == 'month') {
563571
datepickerEl.attr('datepicker-mode', '"month"');
@@ -789,6 +797,8 @@ function ($compile, $parse, $document, $rootScope, $position, dateFilter, datePa
789797
restrict:'EA',
790798
replace: true,
791799
transclude: true,
792-
templateUrl: 'template/datepicker/popup.html'
800+
templateUrl: function(element, attrs) {
801+
return attrs.templateUrl || 'template/datepicker/popup.html';
802+
}
793803
};
794804
});

src/datepicker/docs/readme.md

+15-3
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,14 @@ All settings can be provided as attributes in the `datepicker` or globally confi
8080
* `year-range`
8181
_(Default: 20)_ :
8282
Number of years displayed in year selection.
83-
83+
8484
* `shortcut-propagation`
85-
_(Default: false)_ :
86-
An option to disable or enable shortcut's event propagation.
85+
_(Default: false)_ :
86+
An option to disable or enable shortcut's event propagation.
87+
88+
* `template-url`
89+
_(Default: 'template/datepicker/datepicker.html') :
90+
Allows overriding of default template of the datepicker
8791

8892

8993
### Popup Settings ###
@@ -115,6 +119,14 @@ Specific settings for the `datepicker-popup`, that can globally configured throu
115119
_(Default: true)_ :
116120
Whether to close calendar when a date is chosen.
117121

122+
* `datepicker-popup-template-url`
123+
_(Default: 'template/datepicker/popup.html') :
124+
Allows overriding of default template of the popup
125+
126+
* `datepicker-template-url`
127+
_(Default: 'template/datepicker/popup.html') :
128+
Allows overriding of default template of the datepicker used in popup
129+
118130
* `datepicker-append-to-body`
119131
_(Default: false)_:
120132
Append the datepicker popup element to `body`, rather than inserting after `datepicker-popup`. For global configuration, use `datepickerPopupConfig.appendToBody`.

src/datepicker/test/datepicker.spec.js

+72-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
describe('datepicker directive', function () {
2-
var $rootScope, $compile, element;
2+
var $rootScope, $compile, $templateCache, element;
33
beforeEach(module('ui.bootstrap.datepicker'));
44
beforeEach(module('template/datepicker/datepicker.html'));
55
beforeEach(module('template/datepicker/day.html'));
@@ -198,10 +198,11 @@ describe('datepicker directive', function () {
198198
});
199199

200200
describe('', function () {
201-
beforeEach(inject(function(_$compile_, _$rootScope_) {
201+
beforeEach(inject(function(_$compile_, _$rootScope_, _$templateCache_) {
202202
$compile = _$compile_;
203203
$rootScope = _$rootScope_;
204204
$rootScope.date = new Date('September 30, 2010 15:30:00');
205+
$templateCache = _$templateCache_;
205206
}));
206207

207208

@@ -352,6 +353,31 @@ describe('datepicker directive', function () {
352353
expect(getTitle()).toBe('January 2014');
353354
});
354355

356+
it('should support custom templates', function() {
357+
$templateCache.put('foo/bar.html', '<div>baz</div>');
358+
359+
element = $compile('<datepicker ng-model="date" template-url="foo/bar.html"></datepicker>')($rootScope);
360+
$rootScope.$digest();
361+
362+
expect(element.html()).toBe('baz');
363+
});
364+
365+
it('should expose the controller in the template', function() {
366+
$templateCache.put('template/datepicker/datepicker.html', '<div>{{datepicker.text}}</div>');
367+
368+
element = $compile('<datepicker ng-model="date"></datepicker>')($rootScope);
369+
$rootScope.$digest();
370+
371+
var ctrl = element.controller('datepicker');
372+
expect(ctrl).toBeDefined();
373+
expect(element.html()).toBe('');
374+
375+
ctrl.text = 'baz';
376+
$rootScope.$digest();
377+
378+
expect(element.html()).toBe('baz');
379+
});
380+
355381
// issue #3079
356382
describe('time zone bug', function () {
357383

@@ -2007,6 +2033,50 @@ describe('datepicker directive', function () {
20072033
});
20082034
});
20092035

2036+
describe('with datepicker-popup-template-url', function() {
2037+
beforeEach(function() {
2038+
$rootScope.date = new Date();
2039+
});
2040+
2041+
afterEach(function () {
2042+
$document.find('body').find('.dropdown-menu').remove();
2043+
});
2044+
2045+
it('should allow custom templates for the popup', function() {
2046+
$templateCache.put('foo/bar.html', '<div>baz</div>');
2047+
2048+
var elm = angular.element('<div><input ng-model="date" datepicker-popup datepicker-popup-template-url="foo/bar.html" is-open="true"></div>');
2049+
2050+
$compile(elm)($rootScope);
2051+
$rootScope.$digest();
2052+
2053+
expect(elm.children().eq(1).html()).toBe('baz');
2054+
});
2055+
});
2056+
2057+
describe('with datepicker-template-url', function() {
2058+
beforeEach(function() {
2059+
$rootScope.date = new Date();
2060+
});
2061+
2062+
afterEach(function () {
2063+
$document.find('body').find('.dropdown-menu').remove();
2064+
});
2065+
2066+
it('should allow custom templates for the datepicker', function() {
2067+
$templateCache.put('foo/bar.html', '<div>baz</div>');
2068+
2069+
var elm = angular.element('<div><input ng-model="date" datepicker-popup datepicker-template-url="foo/bar.html" is-open="true"></div>');
2070+
2071+
$compile(elm)($rootScope);
2072+
$rootScope.$digest();
2073+
2074+
var datepicker = elm.find('[datepicker]');
2075+
2076+
expect(datepicker.html()).toBe('baz');
2077+
});
2078+
});
2079+
20102080
describe('with an append-to-body attribute', function() {
20112081
beforeEach(function() {
20122082
$rootScope.date = new Date();

0 commit comments

Comments
 (0)