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

Commit 83c4266

Browse files
andreialecuwesleycho
authored andcommitted
feat(dropdown): support optional templates for dropdown menus
Optionally support specifying a custom template for a dropdown menu. Allows generating multiple dropdown menus with custom templates.
1 parent 1f760eb commit 83c4266

File tree

4 files changed

+80
-6
lines changed

4 files changed

+80
-6
lines changed

src/dropdown/docs/demo.html

+19-1
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,29 @@
5555
<li><a href="#">Separated link</a></li>
5656
</ul>
5757
</div>
58+
59+
<!-- Single button using template-url -->
60+
<div class="btn-group" dropdown>
61+
<button type="button" class="btn btn-primary dropdown-toggle" dropdown-toggle ng-disabled="disabled">
62+
Dropdown using template <span class="caret"></span>
63+
</button>
64+
<ul class="dropdown-menu" template-url="dropdown.html">
65+
</ul>
66+
</div>
5867

5968
<hr />
6069
<p>
6170
<button type="button" class="btn btn-default btn-sm" ng-click="toggleDropdown($event)">Toggle button dropdown</button>
6271
<button type="button" class="btn btn-warning btn-sm" ng-click="disabled = !disabled">Enable/Disable</button>
6372
</p>
64-
73+
74+
<script type="text/ng-template" id="dropdown.html">
75+
<ul class="dropdown-menu" role="menu">
76+
<li><a href="#">Action in Template</a></li>
77+
<li><a href="#">Another action in Template</a></li>
78+
<li><a href="#">Something else here</a></li>
79+
<li class="divider"></li>
80+
<li><a href="#">Separated link in Template</a></li>
81+
</ul>
82+
</script>
6583
</div>

src/dropdown/docs/readme.md

+4
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ By default the dropdown will automatically close if any of its elements is click
1111
* `always` - (Default) automatically closes the dropdown when any of its elements is clicked.
1212
* `outsideClick` - closes the dropdown automatically only when the user clicks any element outside the dropdown.
1313
* `disabled` - disables the auto close. You can then control the open/close status of the dropdown manually, by using `is-open`. Please notice that the dropdown will still close if the toggle is clicked, the `esc` key is pressed or another dropdown is open. The dropdown will no longer close on `$locationChangeSuccess` events.
14+
15+
Optionally, you may specify a template for the dropdown menu using the `template-url` attribute. This is especially useful when you have multiple similar dropdowns in a repeater and you want to keep your HTML output lean and your number of scopes to a minimum. The template has full access to the scope in which the dropdown lies.
16+
17+
Example: `<ul class="dropdown-menu" template-url="custom-dropdown.html"></ul>`.

src/dropdown/dropdown.js

+30-3
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,10 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
6060
};
6161
}])
6262

63-
.controller('DropdownController', ['$scope', '$attrs', '$parse', 'dropdownConfig', 'dropdownService', '$animate', '$position', '$document', function($scope, $attrs, $parse, dropdownConfig, dropdownService, $animate, $position, $document) {
63+
.controller('DropdownController', ['$scope', '$attrs', '$parse', 'dropdownConfig', 'dropdownService', '$animate', '$position', '$document', '$compile', '$templateRequest', function($scope, $attrs, $parse, dropdownConfig, dropdownService, $animate, $position, $document, $compile, $templateRequest) {
6464
var self = this,
6565
scope = $scope.$new(), // create a child scope so we are not polluting original one
66+
templateScope,
6667
openClass = dropdownConfig.openClass,
6768
getIsOpen,
6869
setIsOpen = angular.noop,
@@ -131,9 +132,29 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
131132
$animate[isOpen ? 'addClass' : 'removeClass'](self.$element, openClass);
132133

133134
if ( isOpen ) {
135+
if (self.dropdownMenuTemplateUrl) {
136+
$templateRequest(self.dropdownMenuTemplateUrl).then(function(tplContent) {
137+
templateScope = scope.$new();
138+
$compile(tplContent.trim())(templateScope, function(dropdownElement) {
139+
var newEl = dropdownElement;
140+
self.dropdownMenu.replaceWith(newEl);
141+
self.dropdownMenu = newEl;
142+
});
143+
});
144+
}
145+
134146
scope.focusToggleElement();
135147
dropdownService.open( scope );
136148
} else {
149+
if (self.dropdownMenuTemplateUrl) {
150+
if (templateScope) {
151+
templateScope.$destroy();
152+
}
153+
var newEl = angular.element('<ul class="dropdown-menu"></ul>');
154+
self.dropdownMenu.replaceWith(newEl);
155+
self.dropdownMenu = newEl;
156+
}
157+
137158
dropdownService.close( scope );
138159
}
139160

@@ -168,10 +189,16 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
168189
restrict: 'AC',
169190
require: '?^dropdown',
170191
link: function(scope, element, attrs, dropdownCtrl) {
171-
if ( !dropdownCtrl ) {
192+
if (!dropdownCtrl) {
172193
return;
173194
}
174-
dropdownCtrl.dropdownMenu = element;
195+
var tplUrl = attrs.templateUrl;
196+
if (tplUrl) {
197+
dropdownCtrl.dropdownMenuTemplateUrl = tplUrl;
198+
}
199+
if (!dropdownCtrl.dropdownMenu) {
200+
dropdownCtrl.dropdownMenu = element;
201+
}
175202
}
176203
};
177204
})

src/dropdown/test/dropdown.spec.js

+27-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
describe('dropdownToggle', function() {
2-
var $compile, $rootScope, $document, dropdownConfig, element;
2+
var $compile, $rootScope, $document, $templateCache, dropdownConfig, element;
33

44
beforeEach(module('ui.bootstrap.dropdown'));
55

6-
beforeEach(inject(function(_$compile_, _$rootScope_, _$document_, _dropdownConfig_) {
6+
beforeEach(inject(function(_$compile_, _$rootScope_, _$document_, _$templateCache_, _dropdownConfig_) {
77
$compile = _$compile_;
88
$rootScope = _$rootScope_;
99
$document = _$document_;
10+
$templateCache = _$templateCache_;
1011
dropdownConfig = _dropdownConfig_;
1112
}));
1213

@@ -182,6 +183,30 @@ describe('dropdownToggle', function() {
182183
expect(element.hasClass(dropdownConfig.openClass)).toBe(false);
183184
});
184185
});
186+
187+
describe('using dropdownMenuTemplate', function() {
188+
function dropdown() {
189+
$templateCache.put('custom.html', '<ul class="dropdown-menu"><li>Item 1</li></ul>');
190+
191+
return $compile('<li dropdown><a href dropdown-toggle></a><ul class="dropdown-menu" template-url="custom.html"></ul></li>')($rootScope);
192+
}
193+
194+
beforeEach(function() {
195+
element = dropdown();
196+
});
197+
198+
it('should apply custom template for dropdown menu', function() {
199+
element.find('a').click();
200+
expect(element.find('ul.dropdown-menu').eq(0).find('li').eq(0).text()).toEqual('Item 1');
201+
});
202+
203+
it('should clear ul when dropdown menu is closed', function() {
204+
element.find('a').click();
205+
expect(element.find('ul.dropdown-menu').eq(0).find('li').eq(0).text()).toEqual('Item 1');
206+
element.find('a').click();
207+
expect(element.find('ul.dropdown-menu').eq(0).find('li').length).toEqual(0);
208+
});
209+
});
185210

186211
describe('using dropdown-append-to-body', function() {
187212
function dropdown() {

0 commit comments

Comments
 (0)