Skip to content

Commit

Permalink
fix($parse): one-time binding for literal expressions works as expected
Browse files Browse the repository at this point in the history
Meaning the watcher is only removed when all the properties of the object, or
all the elements of the array, are defined.

Closes angular#8209
  • Loading branch information
rodyhaddad committed Aug 2, 2014
1 parent 108a69b commit c024f28
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 2 deletions.
32 changes: 30 additions & 2 deletions src/ng/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -1012,8 +1012,12 @@ function $ParseProvider() {
var parser = new Parser(lexer, $filter, $parseOptions);
parsedExpression = parser.parse(exp);

if (parsedExpression.constant) parsedExpression.$$watchDelegate = constantWatch;
else if (oneTime) parsedExpression.$$watchDelegate = oneTimeWatch;
if (parsedExpression.constant) {
parsedExpression.$$watchDelegate = constantWatch;
} else if (oneTime) {
parsedExpression.$$watchDelegate = parsedExpression.literal ?
oneTimeLiteralWatch : oneTimeWatch;
}

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

function oneTimeLiteralWatch(scope, listener, objectEquality, parsedExpression) {
var unwatch;
return unwatch = scope.$watch(function oneTimeWatch(scope) {
return parsedExpression(scope);
}, function oneTimeListener(value, old, scope) {
if (isFunction(listener)) {
listener.call(this, value, old, scope);
}
if (isAllDefined(value)) {
scope.$$postDigest(function () {
if(isAllDefined(value)) unwatch();
});
}
}, objectEquality);

function isAllDefined(value) {
var allDefined = true;
forEach(value, function (val) {
if (!isDefined(val)) allDefined = false;
});
return allDefined;
}
}

function constantWatch(scope, listener, objectEquality, parsedExpression) {
var unwatch;
return unwatch = scope.$watch(function constantWatch(scope) {
Expand Down
58 changes: 58 additions & 0 deletions test/ng/parseSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1174,6 +1174,64 @@ describe('parser', function() {
$rootScope.$digest();
expect(fn()).toEqual(null);
}));

describe('literal expressions', function () {
it('should only become stable when all the properties of an object have defined values', inject(function ($parse, $rootScope, log){
var fn = $parse('::{foo: foo, bar: bar}');
$rootScope.$watch(fn, function(value) { log(value); }, true);

expect(log.empty()).toEqual([]);
expect($rootScope.$$watchers.length).toBe(1);

$rootScope.$digest();
expect($rootScope.$$watchers.length).toBe(1);
expect(log.empty()).toEqual([{foo: undefined, bar: undefined}]);

$rootScope.foo = 'foo';
$rootScope.$digest();
expect($rootScope.$$watchers.length).toBe(1);
expect(log.empty()).toEqual([{foo: 'foo', bar: undefined}]);

$rootScope.foo = 'foobar';
$rootScope.bar = 'bar';
$rootScope.$digest();
expect($rootScope.$$watchers.length).toBe(0);
expect(log.empty()).toEqual([{foo: 'foobar', bar: 'bar'}]);

$rootScope.foo = 'baz';
$rootScope.$digest();
expect($rootScope.$$watchers.length).toBe(0);
expect(log.empty()).toEqual([]);
}));

it('should only become stable when all the elements of an array have defined values', inject(function ($parse, $rootScope, log){
var fn = $parse('::[foo,bar]');
$rootScope.$watch(fn, function(value) { log(value); }, true);

expect(log.empty()).toEqual([]);
expect($rootScope.$$watchers.length).toBe(1);

$rootScope.$digest();
expect($rootScope.$$watchers.length).toBe(1);
expect(log.empty()).toEqual([[undefined, undefined]]);

$rootScope.foo = 'foo';
$rootScope.$digest();
expect($rootScope.$$watchers.length).toBe(1);
expect(log.empty()).toEqual([['foo', undefined]]);

$rootScope.foo = 'foobar';
$rootScope.bar = 'bar';
$rootScope.$digest();
expect($rootScope.$$watchers.length).toBe(0);
expect(log.empty()).toEqual([['foobar', 'bar']]);

$rootScope.foo = 'baz';
$rootScope.$digest();
expect($rootScope.$$watchers.length).toBe(0);
expect(log.empty()).toEqual([]);
}));
});
});

describe('locals', function() {
Expand Down

0 comments on commit c024f28

Please sign in to comment.