I use a tooltip directive from Angular UI Bootstrap inside one of my own directives. I want to manually toggle tooltip's visibility using tooltip-is-open
attribute that Bootstrap Tooltip provides. Angular UI documentation states:
tooltip-is-open <WATCHER ICON> (Default: false) - Whether to show the tooltip.
So I assume it watches on the attribute's value. And so I want to bind a scope variable tooltipIsOpen
to the attribute, hoping that changing tooltipIsOpen
value in my directive's link
function would toggle the tooltip visibility.
The behaviour that I get is weird.
If I bind
tooltipIsOpen
value as an expression:tooltip-is-open="{{ tooltipIsOpen }}"
, the tooltip doesn't appear at all, though I see that the DOM is updated properly (tooltip-is-open="false
switches totooltip-is-open="true"
and back again tofalse
) in reaction to mouse events.If I bind
tooltipIsOpen
value as a variable:tooltip-is-open="tooltipIsOpen"
, the tooltip apears properly after the first mouse event, but then doesn't react to any further events, staying displayed all of the time with no way to hide it.The working solution I found is bind
tooltipIsOpen
as a function call :scope.tooltipIsOpenFun = function() { return tooltipIsOpen; }
andtooltip-is-open="tooltipIsOpenFun()"
, or as an object property:tooltip-is-open="tooltip.isOpen"
. Only then the tooltip works fine - shows and hides all of the time.
I suspect it's related to how AngularJS watchers work, also to the difference in how JavaScript primitives and objects are treated when assigning the values. Still I can't understand it.
Question
Can somebody explain to me step by step why the solution 1 doesn't work and 2 works only once?
Directive template (for method 2)
<div uib-tooltip="Tooltip message"
tooltip-is-open="tooltipIsOpened">
...
</div>
Directive link function
module(...).directive(..., function() {
return {
link: function(scope, elem, attr) {
/* Stop further propagation of event */
function catchEvent(event) {
event.stopPropagation();
};
function showTooltip(event) {
scope.$apply(function() {
scope.tooltipIsOpened = true;
/* Hide tooltip on any click outside the select */
angular.element(document).on('click', hideTooltip);
elem.on('click', catchEvent);
});
};
function hideTooltip() {
scope.$apply(function() {
scope.tooltipIsOpened = false;
/* Remove previously attached handlers */
angular.element(document).off('click', hideTooltip);
elem.off('click', catchEvent);
});
};
scope.tooltipIsOpened = false;
elem.on('mouseenter', showTooltip);
elem.on('mouseleave', hideTooltip);
});
$apply()
insideclick
in 2nd method? It is used, only inside aclick
handlerhideTooltip()
.