feat($parse): allow watching array/object of inputs to literal values

The inputs of array/object literals are now watched for changes.
If the an input changes then a new instance of the literal will be
provided when the parsed expression is executed.

Closes #15301
This commit is contained in:
Jason Bedard
2016-10-20 01:59:56 -07:00
committed by Peter Bacon Darwin
parent 7019d9f27a
commit 7084deccaa
2 changed files with 72 additions and 4 deletions
+4 -4
View File
@@ -1782,13 +1782,13 @@ function $ParseProvider() {
}
}
function expressionInputDirtyCheck(newValue, oldValueOfValue) {
function expressionInputDirtyCheck(newValue, oldValueOfValue, compareObjectIdentity) {
if (newValue == null || oldValueOfValue == null) { // null/undefined
return newValue === oldValueOfValue;
}
if (typeof newValue === 'object') {
if (typeof newValue === 'object' && !compareObjectIdentity) {
// attempt to convert the value to a primitive type
// TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
@@ -1817,7 +1817,7 @@ function $ParseProvider() {
inputExpressions = inputExpressions[0];
return scope.$watch(function expressionInputWatch(scope) {
var newInputValue = inputExpressions(scope);
if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf, parsedExpression.literal)) {
lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
oldInputValueOf = newInputValue && getValueOf(newInputValue);
}
@@ -1837,7 +1837,7 @@ function $ParseProvider() {
for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
var newInputValue = inputExpressions[i](scope);
if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i], parsedExpression.literal))) {
oldInputValues[i] = newInputValue;
oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
}
+68
View File
@@ -3118,6 +3118,74 @@ describe('parser', function() {
scope.$digest();
expect(objB.value).toBe(scope.input);
}));
it('should support watching literals', inject(function($parse) {
var lastVal = NaN;
var callCount = 0;
var listener = function(val) { callCount++; lastVal = val; };
scope.$watch('{val: val}', listener);
scope.$apply('val = 1');
expect(callCount).toBe(1);
expect(lastVal).toEqual({val: 1});
scope.$apply('val = []');
expect(callCount).toBe(2);
expect(lastVal).toEqual({val: []});
scope.$apply('val = []');
expect(callCount).toBe(3);
expect(lastVal).toEqual({val: []});
scope.$apply('val = {}');
expect(callCount).toBe(4);
expect(lastVal).toEqual({val: {}});
}));
it('should only watch the direct inputs to literals', inject(function($parse) {
var lastVal = NaN;
var callCount = 0;
var listener = function(val) { callCount++; lastVal = val; };
scope.$watch('{val: val}', listener);
scope.$apply('val = 1');
expect(callCount).toBe(1);
expect(lastVal).toEqual({val: 1});
scope.$apply('val = [2]');
expect(callCount).toBe(2);
expect(lastVal).toEqual({val: [2]});
scope.$apply('val.push(3)');
expect(callCount).toBe(2);
scope.$apply('val.length = 0');
expect(callCount).toBe(2);
}));
it('should only watch the direct inputs to nested literals', inject(function($parse) {
var lastVal = NaN;
var callCount = 0;
var listener = function(val) { callCount++; lastVal = val; };
scope.$watch('[{val: [val]}]', listener);
scope.$apply('val = 1');
expect(callCount).toBe(1);
expect(lastVal).toEqual([{val: [1]}]);
scope.$apply('val = [2]');
expect(callCount).toBe(2);
expect(lastVal).toEqual([{val: [[2]]}]);
scope.$apply('val.push(3)');
expect(callCount).toBe(2);
scope.$apply('val.length = 0');
expect(callCount).toBe(2);
}));
});
describe('locals', function() {