Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 798d277

Browse files
committed
fix($parse): respect the interceptor.$stateful flag
1 parent e58bcfa commit 798d277

File tree

2 files changed

+113
-20
lines changed

2 files changed

+113
-20
lines changed

src/ng/parse.js

+24-20
Original file line numberDiff line numberDiff line change
@@ -1798,15 +1798,9 @@ function $ParseProvider() {
17981798
var lexer = new Lexer($parseOptions);
17991799
var parser = new Parser(lexer, $filter, $parseOptions);
18001800
parsedExpression = parser.parse(exp);
1801-
if (parsedExpression.constant) {
1802-
parsedExpression.$$watchDelegate = constantWatchDelegate;
1803-
} else if (oneTime) {
1804-
parsedExpression.oneTime = true;
1805-
parsedExpression.$$watchDelegate = oneTimeWatchDelegate;
1806-
} else if (parsedExpression.inputs) {
1807-
parsedExpression.$$watchDelegate = inputsWatchDelegate;
1808-
}
1809-
cache[cacheKey] = parsedExpression;
1801+
parsedExpression.oneTime = !!oneTime;
1802+
1803+
cache[cacheKey] = addWatchDelegate(parsedExpression);
18101804
}
18111805
return addInterceptor(parsedExpression, interceptorFn);
18121806

@@ -1931,9 +1925,21 @@ function $ParseProvider() {
19311925
return unwatch;
19321926
}
19331927

1928+
function addWatchDelegate(parsedExpression) {
1929+
if (parsedExpression.constant) {
1930+
parsedExpression.$$watchDelegate = constantWatchDelegate;
1931+
} else if (parsedExpression.oneTime) {
1932+
parsedExpression.$$watchDelegate = oneTimeWatchDelegate;
1933+
} else if (parsedExpression.inputs) {
1934+
parsedExpression.$$watchDelegate = inputsWatchDelegate;
1935+
}
1936+
1937+
return parsedExpression;
1938+
}
1939+
19341940
function addInterceptor(parsedExpression, interceptorFn) {
19351941
if (!interceptorFn) return parsedExpression;
1936-
var watchDelegate = parsedExpression.$$watchDelegate;
1942+
19371943
var useInputs = false;
19381944

19391945
var isDone = parsedExpression.literal ? isAllDefined : isDefined;
@@ -1953,18 +1959,16 @@ function $ParseProvider() {
19531959

19541960
var fn = parsedExpression.oneTime ? oneTimeInterceptedExpression : regularInterceptedExpression;
19551961

1956-
// Propogate the literal/oneTime attributes
1962+
// Propogate the literal/oneTime/constant attributes
19571963
fn.literal = parsedExpression.literal;
19581964
fn.oneTime = parsedExpression.oneTime;
1965+
fn.constant = parsedExpression.constant;
19591966

1960-
// Propagate or create inputs / $$watchDelegates
1961-
useInputs = !parsedExpression.inputs;
1962-
if (watchDelegate && watchDelegate !== inputsWatchDelegate) {
1963-
fn.$$watchDelegate = watchDelegate;
1964-
fn.inputs = parsedExpression.inputs;
1965-
} else if (!interceptorFn.$stateful) {
1966-
// Treat interceptor like filters - assume non-stateful by default and use the inputsWatchDelegate
1967-
fn.$$watchDelegate = inputsWatchDelegate;
1967+
// Treat the interceptor like filters.
1968+
// If it is not $stateful then only watch its inputs.
1969+
// If the expression itself has no inputs then use the full expression as an input.
1970+
if (!interceptorFn.$stateful) {
1971+
useInputs = !parsedExpression.inputs;
19681972
fn.inputs = (parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression]).map(function(e) {
19691973
// Remove the isPure flag of inputs when it is not absolute because they are now wrapped in a
19701974
// potentially non-pure interceptor function.
@@ -1975,7 +1979,7 @@ function $ParseProvider() {
19751979
});
19761980
}
19771981

1978-
return fn;
1982+
return addWatchDelegate(fn);
19791983
}
19801984
}];
19811985
}

test/ng/parseSpec.js

+89
Original file line numberDiff line numberDiff line change
@@ -3256,6 +3256,72 @@ describe('parser', function() {
32563256
expect(called).toBe(true);
32573257
}));
32583258

3259+
it('should always be invoked if flagged as $stateful when wrapping one-time',
3260+
inject(function($parse) {
3261+
3262+
var interceptorCalls = 0;
3263+
function interceptor() {
3264+
interceptorCalls++;
3265+
return 123;
3266+
}
3267+
interceptor.$stateful = true;
3268+
3269+
scope.$watch($parse('::a', interceptor));
3270+
3271+
interceptorCalls = 0;
3272+
scope.$digest();
3273+
expect(interceptorCalls).not.toBe(0);
3274+
3275+
interceptorCalls = 0;
3276+
scope.$digest();
3277+
expect(interceptorCalls).not.toBe(0);
3278+
}));
3279+
3280+
it('should always be invoked if flagged as $stateful when wrapping one-time with inputs',
3281+
inject(function($parse) {
3282+
3283+
$filterProvider.register('identity', valueFn(identity));
3284+
3285+
var interceptorCalls = 0;
3286+
function interceptor() {
3287+
interceptorCalls++;
3288+
return 123;
3289+
}
3290+
interceptor.$stateful = true;
3291+
3292+
scope.$watch($parse('::a | identity', interceptor));
3293+
3294+
interceptorCalls = 0;
3295+
scope.$digest();
3296+
expect(interceptorCalls).not.toBe(0);
3297+
3298+
interceptorCalls = 0;
3299+
scope.$digest();
3300+
expect(interceptorCalls).not.toBe(0);
3301+
}));
3302+
3303+
it('should always be invoked if flagged as $stateful when wrapping one-time literal',
3304+
inject(function($parse) {
3305+
3306+
var interceptorCalls = 0;
3307+
function interceptor() {
3308+
interceptorCalls++;
3309+
return 123;
3310+
}
3311+
interceptor.$stateful = true;
3312+
3313+
scope.$watch($parse('::[a]', interceptor));
3314+
3315+
// Would be great if interceptor-output was checked for changes and this didn't throw...
3316+
interceptorCalls = 0;
3317+
expect(function() { scope.$digest(); }).toThrowMinErr('$rootScope', 'infdig');
3318+
expect(interceptorCalls).not.toBe(0);
3319+
3320+
interceptorCalls = 0;
3321+
expect(function() { scope.$digest(); }).toThrowMinErr('$rootScope', 'infdig');
3322+
expect(interceptorCalls).not.toBe(0);
3323+
}));
3324+
32593325
it('should not be invoked unless the input changes', inject(function($parse) {
32603326
var called = false;
32613327
function interceptor(v) {
@@ -3366,6 +3432,29 @@ describe('parser', function() {
33663432
scope.$digest();
33673433
expect(scope.$$watchersCount).toBe(0);
33683434
}));
3435+
3436+
it('should not propogate $$watchDelegate to the interceptor wrapped expression', inject(function($parse) {
3437+
function getter(s) {
3438+
return s.x;
3439+
}
3440+
getter.$$watchDelegate = getter;
3441+
3442+
function doubler(v) {
3443+
return 2 * v;
3444+
}
3445+
3446+
var lastValue;
3447+
function watcher(val) {
3448+
lastValue = val;
3449+
}
3450+
scope.$watch($parse(getter, doubler), watcher);
3451+
3452+
scope.$apply('x = 1');
3453+
expect(lastValue).toBe(2 * 1);
3454+
3455+
scope.$apply('x = 123');
3456+
expect(lastValue).toBe(2 * 123);
3457+
}));
33693458
});
33703459

33713460
describe('literals', function() {

0 commit comments

Comments
 (0)