diff --git a/src/dropdown/docs/demo.html b/src/dropdown/docs/demo.html index 6487f24cfa..f4feaa9588 100644 --- a/src/dropdown/docs/demo.html +++ b/src/dropdown/docs/demo.html @@ -42,6 +42,20 @@ + +
diff --git a/src/dropdown/docs/readme.md b/src/dropdown/docs/readme.md
index 5f42e0e829..02970d0a5f 100644
--- a/src/dropdown/docs/readme.md
+++ b/src/dropdown/docs/readme.md
@@ -2,8 +2,13 @@
Dropdown is a simple directive which will toggle a dropdown menu on click or programmatically.
You can either use `is-open` to toggle or add inside a `` element to toggle it when is clicked.
There is also the `on-toggle(open)` optional expression fired when dropdown changes state.
+
+Add `dropdown-append-to-body` to the `dropdown` element to append to the inner `dropdown-menu` to the body.
+This is useful when the dropdown button is inside a div with `overflow: hidden`, and the menu would otherwise be hidden.
+
By default the dropdown will automatically close if any of its elements is clicked, you can change this behavior by setting the `auto-close` option as follows:
* `always` - (Default) automatically closes the dropdown when any of its elements is clicked.
* `outsideClick` - closes the dropdown automatically only when the user clicks any element outside the dropdown.
* `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.
+
diff --git a/src/dropdown/dropdown.js b/src/dropdown/dropdown.js
index b469b0bec5..8f03186160 100644
--- a/src/dropdown/dropdown.js
+++ b/src/dropdown/dropdown.js
@@ -1,4 +1,4 @@
-angular.module('ui.bootstrap.dropdown', [])
+angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
.constant('dropdownConfig', {
openClass: 'open'
@@ -60,13 +60,14 @@ angular.module('ui.bootstrap.dropdown', [])
};
}])
-.controller('DropdownController', ['$scope', '$attrs', '$parse', 'dropdownConfig', 'dropdownService', '$animate', function($scope, $attrs, $parse, dropdownConfig, dropdownService, $animate) {
+.controller('DropdownController', ['$scope', '$attrs', '$parse', 'dropdownConfig', 'dropdownService', '$animate', '$position', '$document', function($scope, $attrs, $parse, dropdownConfig, dropdownService, $animate, $position, $document) {
var self = this,
scope = $scope.$new(), // create a child scope so we are not polluting original one
openClass = dropdownConfig.openClass,
getIsOpen,
setIsOpen = angular.noop,
- toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop;
+ toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop,
+ appendToBody = false;
this.init = function( element ) {
self.$element = element;
@@ -79,6 +80,15 @@ angular.module('ui.bootstrap.dropdown', [])
scope.isOpen = !!value;
});
}
+
+ appendToBody = angular.isDefined($attrs.dropdownAppendToBody);
+
+ if ( appendToBody && self.dropdownMenu ) {
+ $document.find('body').append( self.dropdownMenu );
+ element.on('$destroy', function handleDestroyEvent() {
+ self.dropdownMenu.remove();
+ });
+ }
};
this.toggle = function( open ) {
@@ -109,6 +119,15 @@ angular.module('ui.bootstrap.dropdown', [])
};
scope.$watch('isOpen', function( isOpen, wasOpen ) {
+ if ( appendToBody && self.dropdownMenu ) {
+ var pos = $position.positionElements(self.$element, self.dropdownMenu, 'bottom-left', true);
+ self.dropdownMenu.css({
+ top: pos.top + 'px',
+ left: pos.left + 'px',
+ display: isOpen ? 'block' : 'none'
+ });
+ }
+
$animate[isOpen ? 'addClass' : 'removeClass'](self.$element, openClass);
if ( isOpen ) {
@@ -142,6 +161,19 @@ angular.module('ui.bootstrap.dropdown', [])
};
})
+.directive('dropdownMenu', function() {
+ return {
+ restrict: 'AC',
+ require: '?^dropdown',
+ link: function(scope, element, attrs, dropdownCtrl) {
+ if ( !dropdownCtrl ) {
+ return;
+ }
+ dropdownCtrl.dropdownMenu = element;
+ }
+ };
+})
+
.directive('dropdownToggle', function() {
return {
require: '?^dropdown',
diff --git a/src/dropdown/test/dropdown.spec.js b/src/dropdown/test/dropdown.spec.js
index 5e7c7dab9e..8438aca550 100644
--- a/src/dropdown/test/dropdown.spec.js
+++ b/src/dropdown/test/dropdown.spec.js
@@ -9,6 +9,10 @@ describe('dropdownToggle', function() {
$document = _$document_;
}));
+ afterEach(function() {
+ element.remove();
+ });
+
var clickDropdownToggle = function(elm) {
elm = elm || element;
elm.find('a[dropdown-toggle]').click();
@@ -50,7 +54,6 @@ describe('dropdownToggle', function() {
var optionEl = element.find('ul > li').eq(0).find('a').eq(0);
optionEl.click();
expect(element.hasClass('open')).toBe(false);
- element.remove();
});
it('should close on document click', function() {
@@ -66,7 +69,6 @@ describe('dropdownToggle', function() {
triggerKeyDown($document, 27);
expect(element.hasClass('open')).toBe(false);
expect(isFocused(element.find('a'))).toBe(true);
- element.remove();
});
it('should not close on backspace key', function() {
@@ -180,6 +182,26 @@ describe('dropdownToggle', function() {
});
});
+ describe('using dropdown-append-to-body', function() {
+ function dropdown() {
+ return $compile('