From e9881991ca0a5019d3a4215477738ed247898ba0 Mon Sep 17 00:00:00 2001 From: Matias Niemelä Date: Fri, 21 Feb 2014 03:43:50 -0500 Subject: fix($animate): ensure that animateable directives cancel expired leave animations If enter -> leave -> enter -> leave occurs then the first leave animation will animate alongside the second. This causes the very first DOM node (the view in ngView for example) to animate at the same time as the most recent DOM node which ends up being an undesired effect. This fix takes care of this issue. Closes #5886 --- test/ng/directive/ngIfSpec.js | 38 +++++++++++++++++++++++++++++++ test/ng/directive/ngIncludeSpec.js | 40 ++++++++++++++++++++++++++++++++ test/ng/directive/ngSwitchSpec.js | 38 +++++++++++++++++++++++++++++++ test/ngAnimate/animateSpec.js | 35 ++++++++++++++++++++++++++++ test/ngRoute/directive/ngViewSpec.js | 44 ++++++++++++++++++++++++++++++++++++ 5 files changed, 195 insertions(+) (limited to 'test') diff --git a/test/ng/directive/ngIfSpec.js b/test/ng/directive/ngIfSpec.js index d40e6812..771e264a 100755 --- a/test/ng/directive/ngIfSpec.js +++ b/test/ng/directive/ngIfSpec.js @@ -277,4 +277,42 @@ describe('ngIf animations', function () { expect(element.children().length).toBe(0); })); + 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) { + parent.append(element); + } + }); + }); + inject(function ($compile, $rootScope, $animate) { + var item; + var $scope = $rootScope.$new(); + element = $compile(html( + '
' + + '
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 9f37d1fe..510fa38a 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 e039c4d5..2b771a0c 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 55ec4ae8..a30b5fe9 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 259940c6..11a13e40 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); + }); + }); }); -- cgit v1.2.3