fix($parse): do not shallow-watch computed property keys

Shallow watching is not enough when an object implements a non-pure toString
This commit is contained in:
Jason Bedard
2017-07-16 22:15:49 -07:00
parent 341f8dbe24
commit 631076a31b
2 changed files with 30 additions and 9 deletions
+2 -1
View File
@@ -744,7 +744,8 @@ function findConstantAndWatchExpressions(ast, $filter, parentIsPure) {
allConstants = allConstants && property.value.constant;
argsToWatch.push.apply(argsToWatch, property.value.toWatch);
if (property.computed) {
findConstantAndWatchExpressions(property.key, $filter, astIsPure);
//`{[key]: value}` implicitly does `key.toString()` which may be non-pure
findConstantAndWatchExpressions(property.key, $filter, /*parentIsPure=*/false);
allConstants = allConstants && property.key.constant;
argsToWatch.push.apply(argsToWatch, property.key.toWatch);
}
+28 -8
View File
@@ -3805,35 +3805,55 @@ describe('parser', function() {
it('should watch ES6 object computed property changes', function() {
var count = 0;
var values = [];
var lastValue;
scope.$watch('{[a]: true}', function(val) {
count++;
values.push(val);
}, true);
lastValue = val;
});
scope.$digest();
expect(count).toBe(1);
expect(values[0]).toEqual({'undefined': true});
expect(lastValue).toEqual({'undefined': true});
scope.$digest();
expect(count).toBe(1);
expect(values[0]).toEqual({'undefined': true});
expect(lastValue).toEqual({'undefined': true});
scope.a = true;
scope.$digest();
expect(count).toBe(2);
expect(values[1]).toEqual({'true': true});
expect(lastValue).toEqual({'true': true});
scope.a = 'abc';
scope.$digest();
expect(count).toBe(3);
expect(values[2]).toEqual({'abc': true});
expect(lastValue).toEqual({'abc': true});
scope.a = undefined;
scope.$digest();
expect(count).toBe(4);
expect(values[3]).toEqual({'undefined': true});
expect(lastValue).toEqual({'undefined': true});
});
it('should not shallow-watch ES6 object computed properties in case of stateful toString', function() {
var count = 0;
var lastValue;
scope.$watch('{[a]: true}', function(val) {
count++;
lastValue = val;
});
scope.a = {toString: function() { return this.b; }};
scope.a.b = 1;
//TODO: would be great if it didn't throw!
expect(function() { scope.$apply(); }).toThrowMinErr('$rootScope', 'infdig');
expect(lastValue).toEqual({1: true});
expect(function() { scope.$apply('a.b = 2'); }).toThrowMinErr('$rootScope', 'infdig');
expect(lastValue).toEqual({2: true});
});
});