From faf38d20a49176f2016f7f7d4fa49a5c438a986e Mon Sep 17 00:00:00 2001 From: Chris Chua Date: Sun, 2 Nov 2014 01:06:40 -0700 Subject: [PATCH] fix(tooltip): memory leak on show/hide Create a new child scope and retain a reference to it so that it can be destroyed when the tooltip DOM is removed. Fixes #2709 Closes #2919 --- src/tooltip/test/tooltip.spec.js | 10 +++++----- src/tooltip/tooltip.js | 8 +++++++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/tooltip/test/tooltip.spec.js b/src/tooltip/test/tooltip.spec.js index b7f42eb08b..91ebb81f26 100644 --- a/src/tooltip/test/tooltip.spec.js +++ b/src/tooltip/test/tooltip.spec.js @@ -148,11 +148,11 @@ describe('tooltip', function() { expect( elmBody.children().length ).toBe( 0 ); })); - it('issue 1191 - isolate scope on the popup should always be child of correct element scope', function () { + it('issue 1191 - scope on the popup should always be child of correct element scope', function () { var ttScope; elm.trigger( 'mouseenter' ); - ttScope = angular.element( elmBody.children()[1] ).isolateScope(); + ttScope = angular.element( elmBody.children()[1] ).scope(); expect( ttScope.$parent ).toBe( tooltipScope ); elm.trigger( 'mouseleave' ); @@ -160,7 +160,7 @@ describe('tooltip', function() { // After leaving and coming back, the scope's parent should be the same elm.trigger( 'mouseenter' ); - ttScope = angular.element( elmBody.children()[1] ).isolateScope(); + ttScope = angular.element( elmBody.children()[1] ).scope(); expect( ttScope.$parent ).toBe( tooltipScope ); elm.trigger( 'mouseleave' ); @@ -349,7 +349,7 @@ describe('tooltip', function() { var match = false; angular.forEach(angular.element.cache, function (item) { - if (item.data && item.data.$isolateScope === tooltipScope) { + if (item.data && item.data.$scope === tooltipScope) { match = true; } }); @@ -369,7 +369,7 @@ describe('tooltip', function() { tooltipScope = elmScope.$$childTail.$$childTail; })); - it( 'should not contain a cached reference when visible', inject( function( $timeout ) { + it( 'should not contain a cached reference when not visible', inject( function( $timeout ) { expect( inCache() ).toBeTruthy(); elmScope.$destroy(); expect( inCache() ).toBeFalsy(); diff --git a/src/tooltip/tooltip.js b/src/tooltip/tooltip.js index 9980d48c77..6bb486eeea 100644 --- a/src/tooltip/tooltip.js +++ b/src/tooltip/tooltip.js @@ -112,6 +112,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap return function link ( scope, element, attrs ) { var tooltip; + var tooltipLinkedScope; var transitionTimeout; var popupTimeout; var appendToBody = angular.isDefined( options.appendToBody ) ? options.appendToBody : false; @@ -234,7 +235,8 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap if (tooltip) { removeTooltip(); } - tooltip = tooltipLinker(ttScope); + tooltipLinkedScope = ttScope.$new(); + tooltip = tooltipLinker(tooltipLinkedScope); } function removeTooltip() { @@ -243,6 +245,10 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap tooltip.remove(); tooltip = null; } + if (tooltipLinkedScope) { + tooltipLinkedScope.$destroy(); + tooltipLinkedScope = null; + } } function prepareTooltip() {