diff --git a/src/ng/rootScope.js b/src/ng/rootScope.js index 88edcf3faa31..27f8ab992aec 100644 --- a/src/ng/rootScope.js +++ b/src/ng/rootScope.js @@ -490,9 +490,8 @@ function $RootScopeProvider() { } forEach(watchExpressions, function(expr, i) { - var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) { + var unwatchFn = self.$watch(expr, function watchGroupSubAction(value) { newValues[i] = value; - oldValues[i] = oldValue; if (!changeReactionScheduled) { changeReactionScheduled = true; self.$evalAsync(watchGroupAction); @@ -504,11 +503,17 @@ function $RootScopeProvider() { function watchGroupAction() { changeReactionScheduled = false; - if (firstRun) { - firstRun = false; - listener(newValues, newValues, self); - } else { - listener(newValues, oldValues, self); + try { + if (firstRun) { + firstRun = false; + listener(newValues, newValues, self); + } else { + listener(newValues, oldValues, self); + } + } finally { + for (var i = 0; i < watchExpressions.length; i++) { + oldValues[i] = newValues[i]; + } } } diff --git a/test/ng/rootScopeSpec.js b/test/ng/rootScopeSpec.js index 3f5a444f4a58..364196a94b2b 100644 --- a/test/ng/rootScopeSpec.js +++ b/test/ng/rootScopeSpec.js @@ -1097,6 +1097,94 @@ describe('Scope', function() { expect(log).toEqual(''); }); + it('should remove all watchers once one-time/constant bindings are stable', function() { + //empty + scope.$watchGroup([], noop); + //single one-time + scope.$watchGroup(['::a'], noop); + //multi one-time + scope.$watchGroup(['::a', '::b'], noop); + //single constant + scope.$watchGroup(['1'], noop); + //multi constant + scope.$watchGroup(['1', '2'], noop); + + expect(scope.$$watchersCount).not.toBe(0); + scope.$apply('a = b = 1'); + expect(scope.$$watchersCount).toBe(0); + }); + + it('should maintain correct new/old values with one time bindings', function() { + var newValues; + var oldValues; + scope.$watchGroup(['a', '::b', 'b', '4'], function(n, o) { + newValues = n.slice(); + oldValues = o.slice(); + }); + + scope.$apply(); + expect(newValues).toEqual(oldValues); + expect(oldValues).toEqual([undefined, undefined, undefined, 4]); + + scope.$apply('a = 1'); + expect(newValues).toEqual([1, undefined, undefined, 4]); + expect(oldValues).toEqual([undefined, undefined, undefined, 4]); + + scope.$apply('b = 2'); + expect(newValues).toEqual([1, 2, 2, 4]); + expect(oldValues).toEqual([1, undefined, undefined, 4]); + + scope.$apply('b = 3'); + expect(newValues).toEqual([1, 2, 3, 4]); + expect(oldValues).toEqual([1, 2, 2, 4]); + + scope.$apply('b = 4'); + expect(newValues).toEqual([1, 2, 4, 4]); + expect(oldValues).toEqual([1, 2, 3, 4]); + }); + }); + + describe('$watchGroup with logging $exceptionHandler', function() { + it('should maintain correct new/old values even when listener throws', function() { + module(function($exceptionHandlerProvider) { + $exceptionHandlerProvider.mode('log'); + }); + + inject(function($rootScope, $exceptionHandler) { + var newValues; + var oldValues; + $rootScope.$watchGroup(['a', '::b', 'b', '4'], function(n, o) { + newValues = n.slice(); + oldValues = o.slice(); + throw 'test'; + }); + + $rootScope.$apply(); + expect(newValues).toEqual(oldValues); + expect(oldValues).toEqual([undefined, undefined, undefined, 4]); + expect($exceptionHandler.errors.length).toBe(1); + + $rootScope.$apply('a = 1'); + expect(newValues).toEqual([1, undefined, undefined, 4]); + expect(oldValues).toEqual([undefined, undefined, undefined, 4]); + expect($exceptionHandler.errors.length).toBe(2); + + $rootScope.$apply('b = 2'); + expect(newValues).toEqual([1, 2, 2, 4]); + expect(oldValues).toEqual([1, undefined, undefined, 4]); + expect($exceptionHandler.errors.length).toBe(3); + + $rootScope.$apply('b = 3'); + expect(newValues).toEqual([1, 2, 3, 4]); + expect(oldValues).toEqual([1, 2, 2, 4]); + expect($exceptionHandler.errors.length).toBe(4); + + $rootScope.$apply('b = 4'); + expect(newValues).toEqual([1, 2, 4, 4]); + expect(oldValues).toEqual([1, 2, 3, 4]); + expect($exceptionHandler.errors.length).toBe(5); + }); + }); }); describe('$destroy', function() {