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

Commit

Permalink
fix($parse): unwrap promise when setting a field
Browse files Browse the repository at this point in the history
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
  • Loading branch information
James Davies authored and chirayuk committed Jul 31, 2013
1 parent 0bbd20f commit 61906d3
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 0 deletions.
11 changes: 11 additions & 0 deletions src/ng/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
80 changes: 80 additions & 0 deletions test/ng/parseSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down

0 comments on commit 61906d3

Please sign in to comment.