diff --git a/src/popover/docs/readme.md b/src/popover/docs/readme.md
index be398d6c4b..03a52f6108 100644
--- a/src/popover/docs/readme.md
+++ b/src/popover/docs/readme.md
@@ -26,6 +26,9 @@ will display:
`tooltip` directive for supported values.
- `popover-append-to-body`: Should the tooltip be appended to `$body` instead of
the parent element?
+- `popover-is-open`
+ _(Default: false)_:
+ Whether to show the popover.
The popover directives require the `$position` service.
diff --git a/src/popover/test/popover.spec.js b/src/popover/test/popover.spec.js
index bc7f59167e..17e684bfa1 100644
--- a/src/popover/test/popover.spec.js
+++ b/src/popover/test/popover.spec.js
@@ -137,6 +137,37 @@ describe('popover', function() {
}));
});
+
+ describe( 'is-open', function() {
+ beforeEach(inject(function ($compile) {
+ scope.isOpen = false;
+ elmBody = angular.element(
+ '
Trigger here
'
+ );
+ $compile(elmBody)(scope);
+ scope.$digest();
+ elm = elmBody.find('span');
+ elmScope = elm.scope();
+ tooltipScope = elmScope.$$childTail;
+ }));
+
+ it( 'should show and hide with the controller value', function() {
+ expect(tooltipScope.isOpen).toBe(false);
+ elmScope.isOpen = true;
+ elmScope.$digest();
+ expect(tooltipScope.isOpen).toBe(true);
+ elmScope.isOpen = false;
+ elmScope.$digest();
+ expect(tooltipScope.isOpen).toBe(false);
+ });
+
+ it( 'should update the controller value', function() {
+ elm.trigger('click');
+ expect(elmScope.isOpen).toBe(true);
+ elm.trigger('click');
+ expect(elmScope.isOpen).toBe(false);
+ });
+ });
});
diff --git a/src/tooltip/docs/readme.md b/src/tooltip/docs/readme.md
index 4c60b305ea..b0ae385518 100644
--- a/src/tooltip/docs/readme.md
+++ b/src/tooltip/docs/readme.md
@@ -27,6 +27,9 @@ will display:
- `tooltip-append-to-body`: Should the tooltip be appended to `$body` instead of
the parent element?
- `tooltip-class`: Custom class to be applied to the tooltip.
+- `tooltip-is-open`
+ _(Default: false)_:
+ Whether to show the tooltip.
The tooltip directives require the `$position` service.
@@ -38,9 +41,11 @@ provided hide triggers:
- `mouseenter`: `mouseleave`
- `click`: `click`
- `focus`: `blur`
+- `none`: ``
For any non-supported value, the trigger will be used to both show and hide the
-tooltip.
+tooltip. Using the 'none' trigger will disable the internal trigger(s), one can
+then use the `tooltip-is-open` attribute exclusively to show and hide the tooltip.
**$tooltipProvider**
diff --git a/src/tooltip/test/tooltip.spec.js b/src/tooltip/test/tooltip.spec.js
index 578b95ac6b..46d80688e7 100644
--- a/src/tooltip/test/tooltip.spec.js
+++ b/src/tooltip/test/tooltip.spec.js
@@ -307,6 +307,35 @@ describe('tooltip', function() {
}));
});
+
+ describe( 'with an is-open attribute', function() {
+ beforeEach(inject(function ($compile) {
+ scope.isOpen = false;
+ elm = $compile(angular.element(
+ 'Selector Text'
+ ))(scope);
+ elmScope = elm.scope();
+ tooltipScope = elmScope.$$childTail;
+ scope.$digest();
+ }));
+
+ it( 'should show and hide with the controller value', function() {
+ expect(tooltipScope.isOpen).toBe(false);
+ elmScope.isOpen = true;
+ elmScope.$digest();
+ expect(tooltipScope.isOpen).toBe(true);
+ elmScope.isOpen = false;
+ elmScope.$digest();
+ expect(tooltipScope.isOpen).toBe(false);
+ });
+
+ it( 'should update the controller value', function() {
+ elm.trigger('mouseenter');
+ expect(elmScope.isOpen).toBe(true);
+ elm.trigger('mouseleave');
+ expect(elmScope.isOpen).toBe(false);
+ });
+ });
describe( 'with a trigger attribute', function() {
var scope, elmBody, elm, elmScope;
@@ -398,6 +427,20 @@ describe('tooltip', function() {
elm.trigger('fakeTriggerAttr');
expect( tooltipScope.isOpen ).toBeFalsy();
}));
+
+ it( 'should not show when trigger is set to "none"', inject( function( $compile ) {
+ elmBody = angular.element(
+ ''
+ );
+ $compile(elmBody)(scope);
+ scope.$apply();
+ elm = elmBody.find('input');
+ elmScope = elm.scope();
+ tooltipScope = elmScope.$$childTail;
+ expect( tooltipScope.isOpen ).toBeFalsy();
+ elm.trigger('mouseenter');
+ expect( tooltipScope.isOpen ).toBeFalsy();
+ }));
});
describe( 'with an append-to-body attribute', function() {
diff --git a/src/tooltip/tooltip.js b/src/tooltip/tooltip.js
index fce1018f49..0cf9a14b90 100644
--- a/src/tooltip/tooltip.js
+++ b/src/tooltip/tooltip.js
@@ -22,7 +22,8 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
var triggerMap = {
'mouseenter': 'mouseleave',
'click': 'click',
- 'focus': 'blur'
+ 'focus': 'blur',
+ 'none': ''
};
// The options specified to the provider globally.
@@ -65,7 +66,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
* Returns the actual instance of the $tooltip service.
* TODO support multiple triggers
*/
- this.$get = [ '$window', '$compile', '$timeout', '$document', '$position', '$interpolate', '$rootScope', function ( $window, $compile, $timeout, $document, $position, $interpolate, $rootScope ) {
+ this.$get = [ '$window', '$compile', '$timeout', '$document', '$position', '$interpolate', '$rootScope', '$parse', function ( $window, $compile, $timeout, $document, $position, $interpolate, $rootScope, $parse ) {
return function $tooltip ( type, prefix, defaultTriggerShow, options ) {
options = angular.extend( {}, defaultOptions, globalOptions, options );
@@ -127,6 +128,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
var hasEnableExp = angular.isDefined(attrs[prefix+'Enable']);
var ttScope = scope.$new(true);
var repositionScheduled = false;
+ var isOpenExp = angular.isDefined(attrs[prefix + 'IsOpen']) ? $parse(attrs[prefix + 'IsOpen']) : false;
var positionTooltip = function () {
if (!tooltip) { return; }
@@ -211,7 +213,13 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
// And show the tooltip.
ttScope.isOpen = true;
- ttScope.$apply(); // digest required as $apply is not called
+ if (isOpenExp) {
+ isOpenExp.assign(ttScope.origScope, ttScope.isOpen);
+ }
+
+ if (!$rootScope.$$phase) {
+ ttScope.$apply(); // digest required as $apply is not called
+ }
// Return positioning function as promise callback for correct
// positioning after draw.
@@ -222,7 +230,10 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
function hide() {
// First things first: we don't show it anymore.
ttScope.isOpen = false;
-
+ if (isOpenExp) {
+ isOpenExp.assign(ttScope.origScope, ttScope.isOpen);
+ }
+
//if tooltip is going to be shown after delay, we must cancel this
$timeout.cancel( popupTimeout );
popupTimeout = null;
@@ -265,7 +276,9 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
repositionScheduled = true;
tooltipLinkedScope.$$postDigest(function() {
repositionScheduled = false;
- positionTooltipAsync();
+ if (ttScope.isOpen) {
+ positionTooltipAsync();
+ }
});
}
});
@@ -333,6 +346,14 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
}, 0, false);
}
});
+
+ if (isOpenExp) {
+ scope.$watch(isOpenExp, function(val) {
+ if (val !== ttScope.isOpen) {
+ toggleTooltipBind();
+ }
+ });
+ }
function prepPopupClass() {
ttScope.popupClass = attrs[prefix + 'Class'];
@@ -364,14 +385,16 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
triggers = getTriggers( val );
- triggers.show.forEach(function(trigger, idx) {
- if (trigger === triggers.hide[idx]) {
- element.bind(trigger, toggleTooltipBind);
- } else if (trigger) {
- element.bind(trigger, showTooltipBind);
- element.bind(triggers.hide[idx], hideTooltipBind);
- }
- });
+ if (triggers.show !== 'none') {
+ triggers.show.forEach(function(trigger, idx) {
+ if (trigger === triggers.hide[idx]) {
+ element.bind(trigger, toggleTooltipBind);
+ } else if (trigger) {
+ element.bind(trigger, showTooltipBind);
+ element.bind(triggers.hide[idx], hideTooltipBind);
+ }
+ });
+ }
}
prepTriggers();