diff --git a/src/ng/directive/ngIf.js b/src/ng/directive/ngIf.js index 000fba82c6ba..a31015b2c492 100644 --- a/src/ng/directive/ngIf.js +++ b/src/ng/directive/ngIf.js @@ -84,7 +84,7 @@ var ngIfDirective = ['$animate', function($animate) { restrict: 'A', $$tlb: true, link: function ($scope, $element, $attr, ctrl, $transclude) { - var block, childScope; + var block, childScope, previousElements; $scope.$watch($attr.ngIf, function ngIfWatchAction(value) { if (toBoolean(value)) { @@ -102,14 +102,19 @@ var ngIfDirective = ['$animate', function($animate) { }); } } else { - - if (childScope) { + if(previousElements) { + previousElements.remove(); + previousElements = null; + } + if(childScope) { childScope.$destroy(); childScope = null; } - - if (block) { - $animate.leave(getBlockElements(block.clone)); + if(block) { + previousElements = getBlockElements(block.clone); + $animate.leave(previousElements, function() { + previousElements = null; + }); block = null; } } diff --git a/src/ng/directive/ngInclude.js b/src/ng/directive/ngInclude.js index 29e3abce6505..272e199ab210 100644 --- a/src/ng/directive/ngInclude.js +++ b/src/ng/directive/ngInclude.js @@ -177,15 +177,23 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$animate' return function(scope, $element, $attr, ctrl, $transclude) { var changeCounter = 0, currentScope, + previousElement, currentElement; var cleanupLastIncludeContent = function() { - if (currentScope) { + if(previousElement) { + previousElement.remove(); + previousElement = null; + } + if(currentScope) { currentScope.$destroy(); currentScope = null; } if(currentElement) { - $animate.leave(currentElement); + $animate.leave(currentElement, function() { + previousElement = null; + }); + previousElement = currentElement; currentElement = null; } }; diff --git a/src/ng/directive/ngSwitch.js b/src/ng/directive/ngSwitch.js index 925949789ff3..378c0b5024fc 100644 --- a/src/ng/directive/ngSwitch.js +++ b/src/ng/directive/ngSwitch.js @@ -138,12 +138,31 @@ var ngSwitchDirective = ['$animate', function($animate) { var watchExpr = attr.ngSwitch || attr.on, selectedTranscludes, selectedElements, + previousElements, selectedScopes = []; scope.$watch(watchExpr, function ngSwitchWatchAction(value) { - for (var i= 0, ii=selectedScopes.length; i 0) { + if(previousElements) { + for (i = 0; i < ii; i++) { + previousElements[i].remove(); + } + previousElements = null; + } + + previousElements = []; + for (i= 0; i' + + '
Yo
' + + '' + ))($scope); + + $scope.$apply('value = true'); + + var destroyed, inner = element.children(0); + inner.on('$destroy', function() { + destroyed = true; + }); + + $scope.$apply('value = false'); + + $scope.$apply('value = true'); + + $scope.$apply('value = false'); + + expect(destroyed).toBe(true); + }); + }); + }); diff --git a/test/ng/directive/ngIncludeSpec.js b/test/ng/directive/ngIncludeSpec.js index 9f37d1fe606c..510fa38a230d 100644 --- a/test/ng/directive/ngIncludeSpec.js +++ b/test/ng/directive/ngIncludeSpec.js @@ -663,4 +663,44 @@ describe('ngInclude animations', function() { expect(itemA).not.toEqual(itemB); })); + it('should destroy the previous leave animation if a new one takes place', function() { + module(function($provide) { + $provide.value('$animate', { + enabled : function() { return true; }, + leave : function() { + //DOM operation left blank + }, + enter : function(element, parent, after) { + angular.element(after).after(element); + } + }); + }); + inject(function ($compile, $rootScope, $animate, $templateCache) { + var item; + var $scope = $rootScope.$new(); + element = $compile(html( + '
' + + '
Yo
' + + '
' + ))($scope); + + $templateCache.put('one', [200, '
one
', {}]); + $templateCache.put('two', [200, '
two
', {}]); + + $scope.$apply('inc = "one"'); + + var destroyed, inner = element.children(0); + inner.on('$destroy', function() { + destroyed = true; + }); + + $scope.$apply('inc = "two"'); + + $scope.$apply('inc = "one"'); + + $scope.$apply('inc = "two"'); + + expect(destroyed).toBe(true); + }); + }); }); diff --git a/test/ng/directive/ngSwitchSpec.js b/test/ng/directive/ngSwitchSpec.js index e039c4d52932..2b771a0c951c 100644 --- a/test/ng/directive/ngSwitchSpec.js +++ b/test/ng/directive/ngSwitchSpec.js @@ -293,4 +293,42 @@ describe('ngSwitch animations', function() { expect(item.element.text()).toBe('three'); })); + it('should destroy the previous leave animation if a new one takes place', function() { + module(function($provide) { + $provide.value('$animate', { + enabled : function() { return true; }, + leave : function() { + //DOM operation left blank + }, + enter : function(element, parent, after) { + angular.element(after).after(element); + } + }); + }); + inject(function ($compile, $rootScope, $animate, $templateCache) { + var item; + var $scope = $rootScope.$new(); + element = $compile(html( + '
' + + '
one
' + + '
two
' + + '
' + ))($scope); + + $scope.$apply('inc = "one"'); + + var destroyed, inner = element.children(0); + inner.on('$destroy', function() { + destroyed = true; + }); + + $scope.$apply('inc = "two"'); + + $scope.$apply('inc = "one"'); + + $scope.$apply('inc = "two"'); + + expect(destroyed).toBe(true); + }); + }); }); diff --git a/test/ngAnimate/animateSpec.js b/test/ngAnimate/animateSpec.js index 55ec4ae8a22f..a30b5fe99beb 100644 --- a/test/ngAnimate/animateSpec.js +++ b/test/ngAnimate/animateSpec.js @@ -3335,5 +3335,40 @@ describe("ngAnimate", function() { expect(cancelReflowCallback).toHaveBeenCalled(); }); }); + + it('should immediately close off a leave animation if the element is removed from the DOM', function() { + var stat; + module(function($animateProvider) { + $animateProvider.register('.going', function() { + return { + leave : function() { + //left blank so it hangs + stat = 'leaving'; + return function(cancelled) { + stat = cancelled && 'gone'; + }; + } + }; + }); + }); + inject(function($sniffer, $compile, $rootScope, $rootElement, $animate, $timeout) { + + $animate.enabled(true); + + var element = $compile('
')($rootScope); + var child = $compile('
')($rootScope); + $rootElement.append(element); + element.append(child); + + $animate.leave(child); + $rootScope.$digest(); + + expect(stat).toBe('leaving'); + + child.remove(); + + expect(stat).toBe('gone'); + }); + }); }); }); diff --git a/test/ngRoute/directive/ngViewSpec.js b/test/ngRoute/directive/ngViewSpec.js index 259940c646dd..11a13e408cd4 100644 --- a/test/ngRoute/directive/ngViewSpec.js +++ b/test/ngRoute/directive/ngViewSpec.js @@ -833,6 +833,50 @@ describe('ngView animations', function() { } }); }); + + it('should destroy the previous leave animation if a new one takes place', function() { + module(function($provide) { + $provide.value('$animate', { + enabled : function() { return true; }, + leave : function() { + //DOM operation left blank + }, + enter : function(element, parent, after) { + angular.element(after).after(element); + } + }); + }); + inject(function ($compile, $rootScope, $animate, $location) { + var item; + var $scope = $rootScope.$new(); + element = $compile(html( + '
' + + '
' + + '
' + ))($scope); + + $scope.$apply('value = true'); + + $location.path('/bar'); + $rootScope.$digest(); + + var destroyed, inner = element.children(0); + inner.on('$destroy', function() { + destroyed = true; + }); + + $location.path('/foo'); + $rootScope.$digest(); + + $location.path('/bar'); + $rootScope.$digest(); + + $location.path('/bar'); + $rootScope.$digest(); + + expect(destroyed).toBe(true); + }); + }); });