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

Commit c024f28

Browse files
committed
fix($parse): one-time binding for literal expressions works as expected
Meaning the watcher is only removed when all the properties of the object, or all the elements of the array, are defined. Closes #8209
1 parent 108a69b commit c024f28

File tree

2 files changed

+88
-2
lines changed

2 files changed

+88
-2
lines changed

src/ng/parse.js

+30-2
Original file line numberDiff line numberDiff line change
@@ -1012,8 +1012,12 @@ function $ParseProvider() {
10121012
var parser = new Parser(lexer, $filter, $parseOptions);
10131013
parsedExpression = parser.parse(exp);
10141014

1015-
if (parsedExpression.constant) parsedExpression.$$watchDelegate = constantWatch;
1016-
else if (oneTime) parsedExpression.$$watchDelegate = oneTimeWatch;
1015+
if (parsedExpression.constant) {
1016+
parsedExpression.$$watchDelegate = constantWatch;
1017+
} else if (oneTime) {
1018+
parsedExpression.$$watchDelegate = parsedExpression.literal ?
1019+
oneTimeLiteralWatch : oneTimeWatch;
1020+
}
10171021

10181022
if (cacheKey !== 'hasOwnProperty') {
10191023
// Only cache the value if it's not going to mess up the cache object
@@ -1050,6 +1054,30 @@ function $ParseProvider() {
10501054
}, objectEquality);
10511055
}
10521056

1057+
function oneTimeLiteralWatch(scope, listener, objectEquality, parsedExpression) {
1058+
var unwatch;
1059+
return unwatch = scope.$watch(function oneTimeWatch(scope) {
1060+
return parsedExpression(scope);
1061+
}, function oneTimeListener(value, old, scope) {
1062+
if (isFunction(listener)) {
1063+
listener.call(this, value, old, scope);
1064+
}
1065+
if (isAllDefined(value)) {
1066+
scope.$$postDigest(function () {
1067+
if(isAllDefined(value)) unwatch();
1068+
});
1069+
}
1070+
}, objectEquality);
1071+
1072+
function isAllDefined(value) {
1073+
var allDefined = true;
1074+
forEach(value, function (val) {
1075+
if (!isDefined(val)) allDefined = false;
1076+
});
1077+
return allDefined;
1078+
}
1079+
}
1080+
10531081
function constantWatch(scope, listener, objectEquality, parsedExpression) {
10541082
var unwatch;
10551083
return unwatch = scope.$watch(function constantWatch(scope) {

test/ng/parseSpec.js

+58
Original file line numberDiff line numberDiff line change
@@ -1174,6 +1174,64 @@ describe('parser', function() {
11741174
$rootScope.$digest();
11751175
expect(fn()).toEqual(null);
11761176
}));
1177+
1178+
describe('literal expressions', function () {
1179+
it('should only become stable when all the properties of an object have defined values', inject(function ($parse, $rootScope, log){
1180+
var fn = $parse('::{foo: foo, bar: bar}');
1181+
$rootScope.$watch(fn, function(value) { log(value); }, true);
1182+
1183+
expect(log.empty()).toEqual([]);
1184+
expect($rootScope.$$watchers.length).toBe(1);
1185+
1186+
$rootScope.$digest();
1187+
expect($rootScope.$$watchers.length).toBe(1);
1188+
expect(log.empty()).toEqual([{foo: undefined, bar: undefined}]);
1189+
1190+
$rootScope.foo = 'foo';
1191+
$rootScope.$digest();
1192+
expect($rootScope.$$watchers.length).toBe(1);
1193+
expect(log.empty()).toEqual([{foo: 'foo', bar: undefined}]);
1194+
1195+
$rootScope.foo = 'foobar';
1196+
$rootScope.bar = 'bar';
1197+
$rootScope.$digest();
1198+
expect($rootScope.$$watchers.length).toBe(0);
1199+
expect(log.empty()).toEqual([{foo: 'foobar', bar: 'bar'}]);
1200+
1201+
$rootScope.foo = 'baz';
1202+
$rootScope.$digest();
1203+
expect($rootScope.$$watchers.length).toBe(0);
1204+
expect(log.empty()).toEqual([]);
1205+
}));
1206+
1207+
it('should only become stable when all the elements of an array have defined values', inject(function ($parse, $rootScope, log){
1208+
var fn = $parse('::[foo,bar]');
1209+
$rootScope.$watch(fn, function(value) { log(value); }, true);
1210+
1211+
expect(log.empty()).toEqual([]);
1212+
expect($rootScope.$$watchers.length).toBe(1);
1213+
1214+
$rootScope.$digest();
1215+
expect($rootScope.$$watchers.length).toBe(1);
1216+
expect(log.empty()).toEqual([[undefined, undefined]]);
1217+
1218+
$rootScope.foo = 'foo';
1219+
$rootScope.$digest();
1220+
expect($rootScope.$$watchers.length).toBe(1);
1221+
expect(log.empty()).toEqual([['foo', undefined]]);
1222+
1223+
$rootScope.foo = 'foobar';
1224+
$rootScope.bar = 'bar';
1225+
$rootScope.$digest();
1226+
expect($rootScope.$$watchers.length).toBe(0);
1227+
expect(log.empty()).toEqual([['foobar', 'bar']]);
1228+
1229+
$rootScope.foo = 'baz';
1230+
$rootScope.$digest();
1231+
expect($rootScope.$$watchers.length).toBe(0);
1232+
expect(log.empty()).toEqual([]);
1233+
}));
1234+
});
11771235
});
11781236

11791237
describe('locals', function() {

0 commit comments

Comments
 (0)