fix($parse): unwrap promise when setting a field

This fixes an inconsistency where you can't call the setter function
when the expression resolves to a top level field name on a promise.

Setting a field on an unresolved promise will throw an exception.  (This
shouldn't really happen in your template/js code and points to a
programming error.)

Closes #1827
This commit is contained in:
James Davies 2013-06-03 13:04:12 +10:00 committed by Chirayu Krishnappa
parent 0bbd20f255
commit 61906d3517
2 changed files with 91 additions and 0 deletions

View file

@ -766,6 +766,17 @@ function setter(obj, path, setValue, fullExp) {
obj[key] = propertyObj;
}
obj = propertyObj;
if (obj.then) {
if (!("$$v" in obj)) {
(function(promise) {
promise.then(function(val) { promise.$$v = val; }); }
)(obj);
}
if (obj.$$v === undefined) {
obj.$$v = {};
}
obj = obj.$$v;
}
}
key = ensureSafeMemberName(element.shift(), fullExp);
obj[key] = setValue;

View file

@ -845,6 +845,86 @@ describe('parser', function() {
scope.$digest();
expect(scope.$eval('greeting')).toBe(undefined);
});
describe('assignment into promises', function() {
// This behavior is analogous to assignments to non-promise values
// that are lazily set on the scope.
it('should evaluate a resolved object promise and set its value', inject(function($parse) {
scope.person = promise;
deferred.resolve({'name': 'Bill Gates'});
var getter = $parse('person.name');
expect(getter(scope)).toBe(undefined);
scope.$digest();
expect(getter(scope)).toBe('Bill Gates');
getter.assign(scope, 'Warren Buffet');
expect(getter(scope)).toBe('Warren Buffet');
}));
it('should evaluate a resolved primitive type promise and set its value', inject(function($parse) {
scope.greeting = promise;
deferred.resolve('Salut!');
var getter = $parse('greeting');
expect(getter(scope)).toBe(undefined);
scope.$digest();
expect(getter(scope)).toBe('Salut!');
getter.assign(scope, 'Bonjour');
expect(getter(scope)).toBe('Bonjour');
}));
it('should evaluate an unresolved promise and set and remember its value', inject(function($parse) {
scope.person = promise;
var getter = $parse('person.name');
expect(getter(scope)).toBe(undefined);
scope.$digest();
expect(getter(scope)).toBe(undefined);
getter.assign(scope, 'Bonjour');
scope.$digest();
expect(getter(scope)).toBe('Bonjour');
var c1Getter = $parse('person.A.B.C1');
scope.$digest();
expect(c1Getter(scope)).toBe(undefined);
c1Getter.assign(scope, 'c1_value');
scope.$digest();
expect(c1Getter(scope)).toBe('c1_value');
// Set another property on the person.A.B
var c2Getter = $parse('person.A.B.C2');
scope.$digest();
expect(c2Getter(scope)).toBe(undefined);
c2Getter.assign(scope, 'c2_value');
scope.$digest();
expect(c2Getter(scope)).toBe('c2_value');
// c1 should be unchanged.
expect($parse('person.A')(scope)).toEqual(
{B: {C1: 'c1_value', C2: 'c2_value'}});
}));
it('should evaluate a resolved promise and overwrite the previous set value in the absense of the getter',
inject(function($parse) {
scope.person = promise;
var c1Getter = $parse('person.A.B.C1');
c1Getter.assign(scope, 'c1_value');
// resolving the promise should update the tree.
deferred.resolve({A: {B: {C1: 'resolved_c1'}}});
scope.$digest();
expect(c1Getter(scope)).toEqual('resolved_c1');
}));
});
});
describe('dereferencing', function() {