fix($parse): respect the interceptor.$stateful flag

This commit is contained in:
Jason Bedard
2017-05-25 23:43:34 -07:00
parent 529550d0da
commit de74034ddf
2 changed files with 113 additions and 20 deletions
+24 -20
View File
@@ -1798,15 +1798,9 @@ function $ParseProvider() {
var lexer = new Lexer($parseOptions);
var parser = new Parser(lexer, $filter, $parseOptions);
parsedExpression = parser.parse(exp);
if (parsedExpression.constant) {
parsedExpression.$$watchDelegate = constantWatchDelegate;
} else if (oneTime) {
parsedExpression.oneTime = true;
parsedExpression.$$watchDelegate = oneTimeWatchDelegate;
} else if (parsedExpression.inputs) {
parsedExpression.$$watchDelegate = inputsWatchDelegate;
}
cache[cacheKey] = parsedExpression;
parsedExpression.oneTime = !!oneTime;
cache[cacheKey] = addWatchDelegate(parsedExpression);
}
return addInterceptor(parsedExpression, interceptorFn);
@@ -1931,9 +1925,21 @@ function $ParseProvider() {
return unwatch;
}
function addWatchDelegate(parsedExpression) {
if (parsedExpression.constant) {
parsedExpression.$$watchDelegate = constantWatchDelegate;
} else if (parsedExpression.oneTime) {
parsedExpression.$$watchDelegate = oneTimeWatchDelegate;
} else if (parsedExpression.inputs) {
parsedExpression.$$watchDelegate = inputsWatchDelegate;
}
return parsedExpression;
}
function addInterceptor(parsedExpression, interceptorFn) {
if (!interceptorFn) return parsedExpression;
var watchDelegate = parsedExpression.$$watchDelegate;
var useInputs = false;
var isDone = parsedExpression.literal ? isAllDefined : isDefined;
@@ -1953,18 +1959,16 @@ function $ParseProvider() {
var fn = parsedExpression.oneTime ? oneTimeInterceptedExpression : regularInterceptedExpression;
// Propogate the literal/oneTime attributes
// Propogate the literal/oneTime/constant attributes
fn.literal = parsedExpression.literal;
fn.oneTime = parsedExpression.oneTime;
fn.constant = parsedExpression.constant;
// Propagate or create inputs / $$watchDelegates
useInputs = !parsedExpression.inputs;
if (watchDelegate && watchDelegate !== inputsWatchDelegate) {
fn.$$watchDelegate = watchDelegate;
fn.inputs = parsedExpression.inputs;
} else if (!interceptorFn.$stateful) {
// Treat interceptor like filters - assume non-stateful by default and use the inputsWatchDelegate
fn.$$watchDelegate = inputsWatchDelegate;
// Treat the interceptor like filters.
// If it is not $stateful then only watch its inputs.
// If the expression itself has no inputs then use the full expression as an input.
if (!interceptorFn.$stateful) {
useInputs = !parsedExpression.inputs;
fn.inputs = (parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression]).map(function(e) {
// Remove the isPure flag of inputs when it is not absolute because they are now wrapped in a
// potentially non-pure interceptor function.
@@ -1975,7 +1979,7 @@ function $ParseProvider() {
});
}
return fn;
return addWatchDelegate(fn);
}
}];
}
+89
View File
@@ -3324,6 +3324,72 @@ describe('parser', function() {
expect(called).toBe(true);
}));
it('should always be invoked if flagged as $stateful when wrapping one-time',
inject(function($parse) {
var interceptorCalls = 0;
function interceptor() {
interceptorCalls++;
return 123;
}
interceptor.$stateful = true;
scope.$watch($parse('::a', interceptor));
interceptorCalls = 0;
scope.$digest();
expect(interceptorCalls).not.toBe(0);
interceptorCalls = 0;
scope.$digest();
expect(interceptorCalls).not.toBe(0);
}));
it('should always be invoked if flagged as $stateful when wrapping one-time with inputs',
inject(function($parse) {
$filterProvider.register('identity', valueFn(identity));
var interceptorCalls = 0;
function interceptor() {
interceptorCalls++;
return 123;
}
interceptor.$stateful = true;
scope.$watch($parse('::a | identity', interceptor));
interceptorCalls = 0;
scope.$digest();
expect(interceptorCalls).not.toBe(0);
interceptorCalls = 0;
scope.$digest();
expect(interceptorCalls).not.toBe(0);
}));
it('should always be invoked if flagged as $stateful when wrapping one-time literal',
inject(function($parse) {
var interceptorCalls = 0;
function interceptor() {
interceptorCalls++;
return 123;
}
interceptor.$stateful = true;
scope.$watch($parse('::[a]', interceptor));
// Would be great if interceptor-output was checked for changes and this didn't throw...
interceptorCalls = 0;
expect(function() { scope.$digest(); }).toThrowMinErr('$rootScope', 'infdig');
expect(interceptorCalls).not.toBe(0);
interceptorCalls = 0;
expect(function() { scope.$digest(); }).toThrowMinErr('$rootScope', 'infdig');
expect(interceptorCalls).not.toBe(0);
}));
it('should not be invoked unless the input changes', inject(function($parse) {
var called = false;
function interceptor(v) {
@@ -3434,6 +3500,29 @@ describe('parser', function() {
scope.$digest();
expect(scope.$$watchersCount).toBe(0);
}));
it('should not propogate $$watchDelegate to the interceptor wrapped expression', inject(function($parse) {
function getter(s) {
return s.x;
}
getter.$$watchDelegate = getter;
function doubler(v) {
return 2 * v;
}
var lastValue;
function watcher(val) {
lastValue = val;
}
scope.$watch($parse(getter, doubler), watcher);
scope.$apply('x = 1');
expect(lastValue).toBe(2 * 1);
scope.$apply('x = 123');
expect(lastValue).toBe(2 * 123);
}));
});
describe('literals', function() {