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

fix($parse): one-time binding for literal expressions works as expected #8209

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions src/ng/parse.js
Original file line number Diff line number Diff line change
@@ -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
@@ -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) {
58 changes: 58 additions & 0 deletions test/ng/parseSpec.js
Original file line number Diff line number Diff line change
@@ -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() {