From 3b5751dce8d6c699dc76e47cfa544c30b38b9771 Mon Sep 17 00:00:00 2001 From: Lucas Mirelmann Date: Wed, 11 May 2016 22:12:05 +0200 Subject: [PATCH] fix($parse): call once stable bind-once expressions with filter When an expression has the following: - Bind-once - A stateless filter at the end - Optionally an expression interceptor Then call the filter once. Closes: #14583 Closes: #14589 --- src/ng/parse.js | 21 ++++++++++++++------- test/ng/parseSpec.js | 12 ++++++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/ng/parse.js b/src/ng/parse.js index 5615e2f84..99c023cb9 100644 --- a/src/ng/parse.js +++ b/src/ng/parse.js @@ -2050,14 +2050,20 @@ function $ParseProvider() { }, listener, objectEquality, prettyPrintExpression); } - function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) { + function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) { var unwatch, lastValue; - return unwatch = scope.$watch(function oneTimeWatch(scope) { + if (parsedExpression.inputs) { + return unwatch = inputsWatchDelegate(scope, oneTimeListener, objectEquality, parsedExpression, prettyPrintExpression); + } else { + return unwatch = scope.$watch(oneTimeWatch, oneTimeListener, objectEquality); + } + function oneTimeWatch(scope) { return parsedExpression(scope); - }, function oneTimeListener(value, old, scope) { + } + function oneTimeListener(value, old, scope) { lastValue = value; if (isFunction(listener)) { - listener.apply(this, arguments); + listener(value, old, scope); } if (isDefined(value)) { scope.$$postDigest(function() { @@ -2066,7 +2072,7 @@ function $ParseProvider() { } }); } - }, objectEquality); + } } function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) { @@ -2076,7 +2082,7 @@ function $ParseProvider() { }, function oneTimeListener(value, old, scope) { lastValue = value; if (isFunction(listener)) { - listener.call(this, value, old, scope); + listener(value, old, scope); } if (isAllDefined(value)) { scope.$$postDigest(function() { @@ -2123,14 +2129,15 @@ function $ParseProvider() { }; // Propagate $$watchDelegates other then inputsWatchDelegate + useInputs = !parsedExpression.inputs; if (parsedExpression.$$watchDelegate && parsedExpression.$$watchDelegate !== inputsWatchDelegate) { fn.$$watchDelegate = parsedExpression.$$watchDelegate; + fn.inputs = parsedExpression.inputs; } else if (!interceptorFn.$stateful) { // If there is an interceptor, but no watchDelegate then treat the interceptor like // we treat filters - it is assumed to be a pure function unless flagged with $stateful fn.$$watchDelegate = inputsWatchDelegate; - useInputs = !parsedExpression.inputs; fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression]; } diff --git a/test/ng/parseSpec.js b/test/ng/parseSpec.js index ad547a5b6..c823b3a9e 100644 --- a/test/ng/parseSpec.js +++ b/test/ng/parseSpec.js @@ -3232,6 +3232,18 @@ describe('parser', function() { expect(fn()).toEqual(undefined); })); + it('should invoke a stateless filter once when the parsed expression has an interceptor', + inject(function($parse, $rootScope) { + var countFilter = jasmine.createSpy(); + var interceptor = jasmine.createSpy(); + countFilter.and.returnValue(1); + $filterProvider.register('count', valueFn(countFilter)); + $rootScope.foo = function() { return 1; }; + $rootScope.$watch($parse(':: foo() | count', interceptor)); + $rootScope.$digest(); + expect(countFilter.calls.count()).toBe(1); + })); + describe('literal expressions', function() { it('should mark an empty expressions as literal', inject(function($parse) { expect($parse('').literal).toBe(true);