diff --git a/src/popover/test/popover-html.spec.js b/src/popover/test/popover-html.spec.js index c719368173..f76c1e4551 100644 --- a/src/popover/test/popover-html.spec.js +++ b/src/popover/test/popover-html.spec.js @@ -35,16 +35,20 @@ describe('popover', function() { it('should open on click', inject(function() { elm.trigger('click'); - expect( tooltipScope.isOpen ).toBe(true); + tooltipScope.$digest(); + expect(tooltipScope.isOpen).toBe(true); // We can only test *that* the popover-popup element was created as the // implementation is templated and replaced. - expect( elmBody.children().length ).toBe(2); + expect(elmBody.children().length).toBe(2); })); it('should close on second click', inject(function() { elm.trigger('click'); + tooltipScope.$digest(); + expect(tooltipScope.isOpen).toBe(true); elm.trigger('click'); + tooltipScope.$digest(); expect(tooltipScope.isOpen).toBe(false); })); @@ -53,6 +57,7 @@ describe('popover', function() { scope.$digest(); elm.trigger('click'); + tooltipScope.$digest(); expect(tooltipScope.isOpen).toBe(false); expect(elmBody.children().length).toBe(1); @@ -63,6 +68,7 @@ describe('popover', function() { scope.$digest(); elm.trigger('click'); + tooltipScope.$digest(); expect(tooltipScope.isOpen).toBe(true); expect(elmBody.children().eq(1).text().trim()).toBe('My template'); @@ -75,6 +81,7 @@ describe('popover', function() { it('should hide popover when template becomes empty', inject(function($timeout) { elm.trigger('click'); + tooltipScope.$digest(); expect(tooltipScope.isOpen).toBe(true); scope.template = ''; @@ -101,7 +108,9 @@ describe('popover', function() { elm = elmBody.find('input'); elm.trigger('mouseenter'); + tooltipScope.$digest(); elm.trigger('mouseleave'); + tooltipScope.$digest(); expect(scope.clicked).toBeFalsy(); elm.click(); @@ -109,8 +118,9 @@ describe('popover', function() { })); it('should popup with animate class by default', inject(function() { - elm.trigger( 'click' ); - expect( tooltipScope.isOpen ).toBe( true ); + elm.trigger('click'); + tooltipScope.$digest(); + expect(tooltipScope.isOpen).toBe(true); expect(elmBody.children().eq(1)).toHaveClass('fade'); })); @@ -127,6 +137,7 @@ describe('popover', function() { tooltipScope = elmScope.$$childTail; elm.trigger('click'); + tooltipScope.$digest(); expect(tooltipScope.isOpen).toBe(true); expect(elmBody.children().eq(1)).not.toHaveClass('fade'); })); @@ -144,6 +155,7 @@ describe('popover', function() { tooltipScope = elmScope.$$childTail; elm.trigger('click'); + tooltipScope.$digest(); expect(tooltipScope.isOpen).toBe(true); expect(elmBody.children().length).toBe(2); @@ -165,6 +177,7 @@ describe('popover', function() { tooltipScope = elmScope.$$childTail; elm.trigger('click'); + tooltipScope.$digest(); expect(tooltipScope.isOpen).toBe(true); expect(elmBody.children().length).toBe(2); @@ -174,5 +187,3 @@ describe('popover', function() { }); }); }); - - diff --git a/src/popover/test/popover-template.spec.js b/src/popover/test/popover-template.spec.js index f986c5c164..027e260521 100644 --- a/src/popover/test/popover-template.spec.js +++ b/src/popover/test/popover-template.spec.js @@ -32,10 +32,11 @@ describe('popover template', function() { })); it('should open on click', inject(function() { - elm.trigger( 'click' ); - expect( tooltipScope.isOpen ).toBe( true ); + elm.trigger('click'); + tooltipScope.$digest(); + expect(tooltipScope.isOpen).toBe(true); - expect( elmBody.children().length ).toBe( 2 ); + expect(elmBody.children().length ).toBe(2); })); it('should not open on click if templateUrl is empty', inject(function() { @@ -43,6 +44,7 @@ describe('popover template', function() { scope.$digest(); elm.trigger('click'); + tooltipScope.$digest(); expect(tooltipScope.isOpen).toBe(false); expect(elmBody.children().length).toBe(1); @@ -50,11 +52,12 @@ describe('popover template', function() { it('should show updated text', inject(function() { scope.myTemplateText = 'some text'; - scope.$digest(); elm.trigger('click'); + tooltipScope.$digest(); expect(tooltipScope.isOpen).toBe(true); + scope.$digest(); expect(elmBody.children().eq(1).text().trim()).toBe('some text'); scope.myTemplateText = 'new text'; @@ -65,6 +68,7 @@ describe('popover template', function() { it('should hide popover when template becomes empty', inject(function($timeout) { elm.trigger('click'); + tooltipScope.$digest(); expect(tooltipScope.isOpen).toBe(true); scope.templateUrl = ''; @@ -89,6 +93,7 @@ describe('popover template', function() { tooltipScope = elmScope.$$childTail; elm.trigger('click'); + tooltipScope.$digest(); expect(tooltipScope.isOpen).toBe(true); expect(elmBody.children().length).toBe(2); @@ -110,6 +115,7 @@ describe('popover template', function() { tooltipScope = elmScope.$$childTail; elm.trigger('click'); + tooltipScope.$digest(); expect(tooltipScope.isOpen).toBe(true); expect(elmBody.children().length).toBe(2); diff --git a/src/popover/test/popover.spec.js b/src/popover/test/popover.spec.js index b733bb44ac..128b0ddde0 100644 --- a/src/popover/test/popover.spec.js +++ b/src/popover/test/popover.spec.js @@ -34,6 +34,7 @@ describe('popover', function() { it('should open on click', inject(function() { elm.trigger('click'); + tooltipScope.$digest(); expect(tooltipScope.isOpen).toBe(true); // We can only test *that* the popover-popup element was created as the @@ -43,7 +44,10 @@ describe('popover', function() { it('should close on second click', inject(function() { elm.trigger('click'); + tooltipScope.$digest(); + expect(tooltipScope.isOpen).toBe(true); elm.trigger('click'); + tooltipScope.$digest(); expect(tooltipScope.isOpen).toBe(false); })); @@ -70,6 +74,7 @@ describe('popover', function() { it('should popup with animate class by default', inject(function() { elm.trigger('click'); + tooltipScope.$digest(); expect(tooltipScope.isOpen).toBe(true); expect(elmBody.children().eq(1)).toHaveClass('fade'); @@ -87,6 +92,7 @@ describe('popover', function() { tooltipScope = elmScope.$$childTail; elm.trigger('click'); + tooltipScope.$digest(); expect(tooltipScope.isOpen).toBe(true); expect(elmBody.children().eq(1)).not.toHaveClass('fade'); })); @@ -104,6 +110,7 @@ describe('popover', function() { tooltipScope = elmScope.$$childTail; elm.trigger('click'); + tooltipScope.$digest(); expect(tooltipScope.isOpen).toBe(true); expect(elmBody.children().length).toBe(2); @@ -124,6 +131,7 @@ describe('popover', function() { tooltipScope = elmScope.$$childTail; elm.trigger('click'); + tooltipScope.$digest(); expect(tooltipScope.isOpen).toBe(true); expect(elmBody.children().length).toBe(2); @@ -131,8 +139,8 @@ describe('popover', function() { expect(ttipElement).toHaveClass('custom'); })); }); - - describe( 'is-open', function() { + + describe('is-open', function() { beforeEach(inject(function ($compile) { scope.isOpen = false; elmBody = angular.element( @@ -144,8 +152,8 @@ describe('popover', function() { elmScope = elm.scope(); tooltipScope = elmScope.$$childTail; })); - - it( 'should show and hide with the controller value', function() { + + it('should show and hide with the controller value', function() { expect(tooltipScope.isOpen).toBe(false); elmScope.isOpen = true; elmScope.$digest(); @@ -154,11 +162,13 @@ describe('popover', function() { elmScope.$digest(); expect(tooltipScope.isOpen).toBe(false); }); - - it( 'should update the controller value', function() { + + it('should update the controller value', function() { elm.trigger('click'); + tooltipScope.$digest(); expect(elmScope.isOpen).toBe(true); elm.trigger('click'); + tooltipScope.$digest(); expect(elmScope.isOpen).toBe(false); }); }); diff --git a/src/tooltip/test/tooltip-template.spec.js b/src/tooltip/test/tooltip-template.spec.js index 3d770cfcef..79c1d2c702 100644 --- a/src/tooltip/test/tooltip-template.spec.js +++ b/src/tooltip/test/tooltip-template.spec.js @@ -34,6 +34,7 @@ describe('tooltip template', function() { evt = new Event(evt); element[0].dispatchEvent(evt); + element.scope().$$childTail.$digest(); } it('should open on mouseenter', inject(function() { @@ -55,10 +56,10 @@ describe('tooltip template', function() { it('should show updated text', inject(function() { scope.myTemplateText = 'some text'; - scope.$digest(); trigger(elm, 'mouseenter'); expect(tooltipScope.isOpen).toBe(true); + scope.$digest(); expect(elmBody.children().eq(1).text().trim()).toBe('some text'); diff --git a/src/tooltip/test/tooltip.spec.js b/src/tooltip/test/tooltip.spec.js index 4c50477c72..6362fd885e 100644 --- a/src/tooltip/test/tooltip.spec.js +++ b/src/tooltip/test/tooltip.spec.js @@ -30,6 +30,7 @@ describe('tooltip', function() { evt = new Event(evt); element[0].dispatchEvent(evt); + element.scope().$$childTail.$digest(); } it('should not be open initially', inject(function() { @@ -78,7 +79,7 @@ describe('tooltip', function() { it('should update placement dynamically', inject(function($compile, $timeout) { scope.place = 'bottom'; - elm = $compile( angular.element( + elm = $compile(angular.element( 'Selector Text' ))(scope); scope.$apply(); @@ -110,15 +111,15 @@ describe('tooltip', function() { scope.$digest(); var tt = angular.element(elm.find('li > span')[0]); - - tt.trigger('mouseenter'); + trigger(tt, 'mouseenter'); expect(tt.text()).toBe(scope.items[0].name); tooltipScope = tt.scope().$$childTail; expect(tooltipScope.content).toBe(scope.items[0].tooltip); - tt.trigger('mouseleave'); + trigger(tt, 'mouseleave'); + expect(tooltipScope.isOpen).toBeFalsy(); })); it('should show correct text when in an ngRepeat', inject(function($compile, $timeout) { @@ -163,7 +164,7 @@ describe('tooltip', function() { scope.tooltipMsg = 'Tooltip Text'; scope.alt = 'Alt Message'; - elmBody = $compile( angular.element( + elmBody = $compile(angular.element( '
Selector Text
' ))(scope); @@ -331,11 +332,13 @@ describe('tooltip', function() { scope.$digest(); trigger(elm, 'mouseenter'); + tooltipScope2.$digest(); $timeout.flush(); expect(tooltipScope.isOpen).toBe(true); expect(tooltipScope2.isOpen).toBe(false); trigger(elm2, 'mouseenter'); + tooltipScope2.$digest(); $timeout.flush(); expect(tooltipScope.isOpen).toBe(true); expect(tooltipScope2.isOpen).toBe(true); @@ -344,6 +347,8 @@ describe('tooltip', function() { evt.which = 27; $document.trigger(evt); + tooltipScope.$digest(); + tooltipScope2.$digest(); expect(tooltipScope.isOpen).toBe(true); expect(tooltipScope2.isOpen).toBe(false); @@ -352,6 +357,8 @@ describe('tooltip', function() { evt2.which = 27; $document.trigger(evt2); + tooltipScope.$digest(); + tooltipScope2.$digest(); expect(tooltipScope.isOpen).toBe(false); expect(tooltipScope2.isOpen).toBe(false); @@ -579,6 +586,46 @@ describe('tooltip', function() { expect(inCache()).toBeFalsy(); })); }); + + describe('observers', function() { + var elmBody, elm, elmScope, scope, tooltipScope; + + beforeEach(inject(function($compile, $rootScope) { + scope = $rootScope; + scope.content = 'tooltip content'; + scope.placement = 'top'; + elmBody = angular.element('
'); + $compile(elmBody)(scope); + scope.$apply(); + + elm = elmBody.find('input'); + elmScope = elm.scope(); + tooltipScope = elmScope.$$childTail; + })); + + it('should be removed when tooltip hides', inject(function($timeout) { + expect(tooltipScope.content).toBe(undefined); + expect(tooltipScope.placement).toBe(undefined); + + trigger(elm, 'mouseenter'); + expect(tooltipScope.content).toBe('tooltip content'); + expect(tooltipScope.placement).toBe('top'); + scope.content = 'tooltip content updated'; + + scope.placement = 'bottom'; + scope.$apply(); + expect(tooltipScope.content).toBe('tooltip content updated'); + expect(tooltipScope.placement).toBe('bottom'); + + trigger(elm, 'mouseleave'); + $timeout.flush(); + scope.content = 'tooltip content updated after close'; + scope.placement = 'left'; + scope.$apply(); + expect(tooltipScope.content).toBe('tooltip content updated'); + expect(tooltipScope.placement).toBe('bottom'); + })); + }); }); describe('tooltipWithDifferentSymbols', function() { @@ -600,6 +647,7 @@ describe('tooltipWithDifferentSymbols', function() { evt = new Event(evt); element[0].dispatchEvent(evt); + element.scope().$$childTail.$digest(); } it('should show the correct tooltip text', inject(function($compile, $rootScope) { @@ -634,9 +682,9 @@ describe('tooltip positioning', function() { scope = $rootScope; scope.text = 'Some Text'; - elmBody = $compile( angular.element( + elmBody = $compile(angular.element( '
Selector Text
' - ))( scope); + ))(scope); scope.$digest(); elm = elmBody.find('span'); elmScope = elm.scope(); @@ -647,6 +695,7 @@ describe('tooltip positioning', function() { evt = new Event(evt); element[0].dispatchEvent(evt); + element.scope().$$childTail.$digest(); } it('should re-position when value changes', inject(function($timeout) { @@ -659,7 +708,7 @@ describe('tooltip positioning', function() { scope.text = 'New Text'; scope.$digest(); $timeout.flush(); - expect(elm.attr('tooltip')).toBe( 'New Text' ); + expect(elm.attr('tooltip')).toBe('New Text' ); expect($position.positionElements.calls.count()).toEqual(startingPositionCalls + 1); // Check that positionElements was called with elm expect($position.positionElements.calls.argsFor(startingPositionCalls)[0][0]) @@ -691,9 +740,9 @@ describe('tooltipHtml', function() { scope.html = 'I say: Hello!'; scope.safeHtml = $sce.trustAsHtml(scope.html); - elmBody = $compile( angular.element( + elmBody = $compile(angular.element( '
Selector Text
' - ))( scope ); + ))(scope); scope.$digest(); elm = elmBody.find('span'); elmScope = elm.scope(); @@ -704,12 +753,13 @@ describe('tooltipHtml', function() { evt = new Event(evt); element[0].dispatchEvent(evt); + element.scope().$$childTail.$digest(); } it('should render html properly', inject(function() { trigger(elm, 'mouseenter'); - expect( elmBody.find('.tooltip-inner').html()).toBe(scope.html); + expect(elmBody.find('.tooltip-inner').html()).toBe(scope.html); })); it('should not open if html is empty', function() { @@ -745,6 +795,7 @@ describe('$tooltipProvider', function() { evt = new Event(evt); element[0].dispatchEvent(evt); + element.scope().$$childTail.$digest(); } describe('popupDelay', function() { diff --git a/src/tooltip/test/tooltip2.spec.js b/src/tooltip/test/tooltip2.spec.js index 354adbcf24..5554be0849 100644 --- a/src/tooltip/test/tooltip2.spec.js +++ b/src/tooltip/test/tooltip2.spec.js @@ -45,6 +45,7 @@ describe('tooltip directive', function() { function closeTooltip(hostEl, triggerEvt, shouldNotFlush) { trigger(hostEl, triggerEvt || 'mouseleave'); + hostEl.scope().$$childTail.$digest(); if (!shouldNotFlush) { $timeout.flush(); } @@ -54,6 +55,7 @@ describe('tooltip directive', function() { evt = new Event(evt); element[0].dispatchEvent(evt); + element.scope().$$childTail.$digest(); } describe('basic scenarios with default options', function() { diff --git a/src/tooltip/tooltip.js b/src/tooltip/tooltip.js index 22edd6b1c9..eb4f8d29cf 100644 --- a/src/tooltip/tooltip.js +++ b/src/tooltip/tooltip.js @@ -79,7 +79,7 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s } }); - return function $tooltip(type, prefix, defaultTriggerShow, options) { + return function $tooltip(ttType, prefix, defaultTriggerShow, options) { options = angular.extend({}, defaultOptions, globalOptions, options); /** @@ -107,7 +107,7 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s }; } - var directiveName = snake_case(type); + var directiveName = snake_case(ttType); var startSym = $interpolate.startSymbol(); var endSym = $interpolate.endSymbol(); @@ -142,7 +142,9 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s 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 isOpenParse = angular.isDefined(attrs[prefix + 'IsOpen']) ? $parse(attrs[prefix + 'IsOpen']) : false; + var contentParse = options.useContentExp ? $parse(attrs[ttType]) : false; + var observers = []; var positionTooltip = function() { // check if tooltip exists and is not empty @@ -150,24 +152,17 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s if (!positionTimeout) { positionTimeout = $timeout(function() { - // Reset the positioning and box size for correct width and height values. - tooltip.css({ top: 0, left: 0, width: 'auto', height: 'auto', visibility: 'hidden' }); + // Reset the positioning. + tooltip.css({ top: 0, left: 0 }); - var ttBox = $position.position(tooltip); + // Now set the calculated positioning. var ttCss = $position.positionElements(element, tooltip, ttScope.placement, appendToBody); ttCss.top += 'px'; ttCss.left += 'px'; - - ttCss.width = ttBox.width + 'px'; - ttCss.height = ttBox.height + 'px'; - ttCss.visibility = 'visible'; - - // Now set the calculated positioning and size. tooltip.css(ttCss); positionTimeout = null; - }, 0, false); } }; @@ -179,7 +174,7 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s // TODO add ability to start tooltip opened ttScope.isOpen = false; openedTooltips.add(ttScope, { - close: hideTooltipBind + close: hide }); function toggleTooltipBind() { @@ -211,9 +206,6 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s function hideTooltipBind() { hide(); - if (!$rootScope.$$phase) { - $rootScope.$digest(); - } } // Show the tooltip popup element. @@ -231,23 +223,21 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s } // Don't show empty tooltips. - if (!(options.useContentExp ? ttScope.contentExp() : ttScope.content)) { + if (!ttScope.content) { return angular.noop; } createTooltip(); // And show the tooltip. - ttScope.isOpen = true; - if (isOpenExp && angular.isFunction(isOpenExp.assign)) { - isOpenExp.assign(ttScope.origScope, ttScope.isOpen); - } - - if (!$rootScope.$$phase) { - ttScope.$apply(); // digest required as $apply is not called - } + ttScope.$evalAsync(function() { + ttScope.isOpen = true; + if (isOpenParse && angular.isFunction(isOpenParse.assign)) { + isOpenParse.assign(ttScope.origScope, ttScope.isOpen); + } - positionTooltip(); + positionTooltip(); + }); } // Hide the tooltip popup element. @@ -257,10 +247,12 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s } // First things first: we don't show it anymore. - ttScope.isOpen = false; - if (isOpenExp && angular.isFunction(isOpenExp.assign)) { - isOpenExp.assign(ttScope.origScope, ttScope.isOpen); - } + ttScope.$evalAsync(function() { + ttScope.isOpen = false; + if (isOpenParse && angular.isFunction(isOpenParse.assign)) { + isOpenParse.assign(ttScope.origScope, ttScope.isOpen); + } + }); //if tooltip is going to be shown after delay, we must cancel this $timeout.cancel(popupTimeout); @@ -284,8 +276,9 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s function createTooltip() { // There can only be one tooltip element per directive shown at once. if (tooltip) { - removeTooltip(); + return; } + tooltipLinkedScope = ttScope.$new(); tooltip = tooltipLinker(tooltipLinkedScope, function(tooltip) { if (appendToBody) { @@ -295,29 +288,12 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s } }); - if (options.useContentExp) { - tooltipLinkedScope.$watch('contentExp()', function(val) { - if (!val && ttScope.isOpen) { - hide(); - } - }); - - tooltipLinkedScope.$watch(function() { - if (!repositionScheduled) { - repositionScheduled = true; - tooltipLinkedScope.$$postDigest(function() { - repositionScheduled = false; - if (ttScope.isOpen) { - positionTooltip(); - } - }); - } - }); - - } + prepObservers(); } function removeTooltip() { + unregisterObservers(); + transitionTimeout = null; if (tooltip) { tooltip.remove(); @@ -329,31 +305,32 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s } } + /** + * Set the inital scope values. Once + * the tooltip is created, the observers + * will be added to keep things in synch. + */ function prepareTooltip() { - prepPopupClass(); - prepPlacement(); - prepPopupDelay(); + ttScope.title = attrs[prefix + 'Title']; + if (contentParse) { + ttScope.content = contentParse(scope); + } else { + ttScope.content = attrs[ttType]; + } + ttScope.popupClass = attrs[prefix + 'Class']; + ttScope.placement = angular.isDefined(attrs[prefix + 'Placement']) ? attrs[prefix + 'Placement'] : options.placement; + + var delay = parseInt(attrs[prefix + 'PopupDelay'], 10); + ttScope.popupDelay = !isNaN(delay) ? delay : options.popupDelay; } ttScope.contentExp = function() { - return scope.$eval(attrs[type]); + return ttScope.content; }; /** * Observe the relevant attributes. */ - if (!options.useContentExp) { - attrs.$observe(type, function(val) { - ttScope.content = val; - - if (!val && ttScope.isOpen) { - hide(); - } else { - positionTooltip(); - } - }); - } - attrs.$observe('disabled', function(val) { if (popupTimeout && val) { $timeout.cancel(popupTimeout); @@ -365,39 +342,80 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s } }); - attrs.$observe(prefix + 'Title', function(val) { - ttScope.title = val; - positionTooltip(); - }); - - attrs.$observe(prefix + 'Placement', function() { - if (ttScope.isOpen) { - prepPlacement(); - positionTooltip(); - } - }); - - if (isOpenExp) { - scope.$watch(isOpenExp, function(val) { - if (val !== ttScope.isOpen) { + if (isOpenParse) { + scope.$watch(isOpenParse, function(val) { + /*jshint -W018 */ + if (!val === ttScope.isOpen) { toggleTooltipBind(); } }); } - function prepPopupClass() { - ttScope.popupClass = attrs[prefix + 'Class']; - } + function prepObservers() { + observers.length = 0; + + if (contentParse) { + observers.push( + scope.$watch(contentParse, function(val) { + ttScope.content = val; + if (!val && ttScope.isOpen) { + hide(); + } + }) + ); + + observers.push( + tooltipLinkedScope.$watch(function() { + if (!repositionScheduled) { + repositionScheduled = true; + tooltipLinkedScope.$$postDigest(function() { + repositionScheduled = false; + if (ttScope.isOpen) { + positionTooltip(); + } + }); + } + }) + ); + } else { + observers.push( + attrs.$observe(ttType, function(val) { + ttScope.content = val; + if (!val && ttScope.isOpen) { + hide(); + } else { + positionTooltip(); + } + }) + ); + } - function prepPlacement() { - var val = attrs[prefix + 'Placement']; - ttScope.placement = angular.isDefined(val) ? val : options.placement; + observers.push( + attrs.$observe(prefix + 'Title', function(val) { + ttScope.title = val; + if (ttScope.isOpen) { + positionTooltip(); + } + }) + ); + + observers.push( + attrs.$observe(prefix + 'Placement', function(val) { + ttScope.placement = val ? val : options.placement; + if (ttScope.isOpen) { + positionTooltip(); + } + }) + ); } - function prepPopupDelay() { - var val = attrs[prefix + 'PopupDelay']; - var delay = parseInt(val, 10); - ttScope.popupDelay = !isNaN(delay) ? delay : options.popupDelay; + function unregisterObservers() { + if (observers.length) { + angular.forEach(observers, function(observer) { + observer(); + }); + observers.length = 0; + } } var unregisterTriggers = function() {