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
This commit is contained in:
Lucas Mirelmann
2016-05-11 22:12:05 +02:00
parent 5d1e15cb1b
commit 3b5751dce8
2 changed files with 26 additions and 7 deletions
+14 -7
View File
@@ -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];
}
+12
View File
@@ -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);