aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/service/parse.js29
-rw-r--r--test/service/parseSpec.js165
2 files changed, 189 insertions, 5 deletions
diff --git a/src/service/parse.js b/src/service/parse.js
index 432929b5..3a6c4106 100644
--- a/src/service/parse.js
+++ b/src/service/parse.js
@@ -522,9 +522,21 @@ function parser(text, json, $filter){
consume(']');
return extend(
function(self){
- var o = obj(self);
- var i = indexFn(self);
- return (o) ? o[i] : undefined;
+ var o = obj(self),
+ i = indexFn(self),
+ v, p;
+
+ if (!o) return undefined;
+ v = o[i];
+ if (v && v.then) {
+ p = v;
+ if (!('$$v' in v)) {
+ p.$$v = undefined;
+ p.then(function(val) { p.$$v = val; });
+ }
+ v = v.$$v;
+ }
+ return v;
}, {
assign:function(self, value){
return obj(self)[indexFn(self)] = value;
@@ -673,7 +685,7 @@ function getterFn(path) {
var fn = getterFnCache[path];
if (fn) return fn;
- var code = 'var l, fn, t;\n';
+ var code = 'var l, fn, p;\n';
forEach(path.split('.'), function(key) {
key = (JS_KEYWORDS[key]) ? '["' + key + '"]' : '.' + key;
code += 'if(!s) return s;\n' +
@@ -683,11 +695,18 @@ function getterFn(path) {
' fn=function(){ return l' + key + '.apply(l, arguments); };\n' +
' fn.$unboundFn=s;\n' +
' s=fn;\n' +
+ '} else if (s && s.then) {\n' +
+ ' if (!("$$v" in s)) {\n' +
+ ' p=s;\n' +
+ ' p.$$v = undefined;\n' +
+ ' p.then(function(v) {p.$$v=v;});\n' +
+ '}\n' +
+ ' s=s.$$v\n' +
'}\n';
});
code += 'return s;';
fn = Function('s', code);
- fn["toString"] = function() { return code; };
+ fn.toString = function() { return code; };
return getterFnCache[path] = fn;
}
diff --git a/test/service/parseSpec.js b/test/service/parseSpec.js
index 2742f381..85dab72c 100644
--- a/test/service/parseSpec.js
+++ b/test/service/parseSpec.js
@@ -407,6 +407,171 @@ describe('parser', function() {
});
+ describe('promises', function() {
+ var deferred, promise, q;
+
+ beforeEach(inject(function($q) {
+ q = $q;
+ deferred = q.defer();
+ promise = deferred.promise;
+ }));
+
+ describe('{{promise}}', function() {
+ it('should evaluated resolved promise and get its value', function() {
+ deferred.resolve('hello!');
+ scope.greeting = promise;
+ expect(scope.$eval('greeting')).toBe(undefined);
+ scope.$digest();
+ expect(scope.$eval('greeting')).toBe('hello!');
+ });
+
+
+ it('should evaluated rejected promise and ignore the rejection reason', function() {
+ deferred.reject('sorry');
+ scope.greeting = promise;
+ expect(scope.$eval('gretting')).toBe(undefined);
+ scope.$digest();
+ expect(scope.$eval('greeting')).toBe(undefined);
+ });
+
+
+ it('should evaluate a promise and eventualy get its value', function() {
+ scope.greeting = promise;
+ expect(scope.$eval('greeting')).toBe(undefined);
+
+ scope.$digest();
+ expect(scope.$eval('greeting')).toBe(undefined);
+
+ deferred.resolve('hello!');
+ expect(scope.$eval('greeting')).toBe(undefined);
+ scope.$digest();
+ expect(scope.$eval('greeting')).toBe('hello!');
+ });
+
+
+ it('should evaluate a promise and eventualy ignore its rejection', function() {
+ scope.greeting = promise;
+ expect(scope.$eval('greeting')).toBe(undefined);
+
+ scope.$digest();
+ expect(scope.$eval('greeting')).toBe(undefined);
+
+ deferred.reject('sorry');
+ expect(scope.$eval('greeting')).toBe(undefined);
+ scope.$digest();
+ expect(scope.$eval('greeting')).toBe(undefined);
+ });
+ });
+
+ describe('dereferencing', function() {
+ it('should evaluate and dereference properties leading to and from a promise', function() {
+ scope.obj = {greeting: promise};
+ expect(scope.$eval('obj.greeting')).toBe(undefined);
+ expect(scope.$eval('obj.greeting.polite')).toBe(undefined);
+
+ scope.$digest();
+ expect(scope.$eval('obj.greeting')).toBe(undefined);
+ expect(scope.$eval('obj.greeting.polite')).toBe(undefined);
+
+ deferred.resolve({polite: 'Good morning!'});
+ scope.$digest();
+ expect(scope.$eval('obj.greeting')).toEqual({polite: 'Good morning!'});
+ expect(scope.$eval('obj.greeting.polite')).toBe('Good morning!');
+ });
+
+ it('should evaluate and dereference properties leading to and from a promise via bracket ' +
+ 'notation', function() {
+ scope.obj = {greeting: promise};
+ expect(scope.$eval('obj["greeting"]')).toBe(undefined);
+ expect(scope.$eval('obj["greeting"]["polite"]')).toBe(undefined);
+
+ scope.$digest();
+ expect(scope.$eval('obj["greeting"]')).toBe(undefined);
+ expect(scope.$eval('obj["greeting"]["polite"]')).toBe(undefined);
+
+ deferred.resolve({polite: 'Good morning!'});
+ scope.$digest();
+ expect(scope.$eval('obj["greeting"]')).toEqual({polite: 'Good morning!'});
+ expect(scope.$eval('obj["greeting"]["polite"]')).toBe('Good morning!');
+ });
+
+
+ it('should evaluate and dereference array references leading to and from a promise',
+ function() {
+ scope.greetings = [promise];
+ expect(scope.$eval('greetings[0]')).toBe(undefined);
+ expect(scope.$eval('greetings[0][0]')).toBe(undefined);
+
+ scope.$digest();
+ expect(scope.$eval('greetings[0]')).toBe(undefined);
+ expect(scope.$eval('greetings[0][0]')).toBe(undefined);
+
+ deferred.resolve(['Hi!', 'Cau!']);
+ scope.$digest();
+ expect(scope.$eval('greetings[0]')).toEqual(['Hi!', 'Cau!']);
+ expect(scope.$eval('greetings[0][0]')).toBe('Hi!');
+ });
+
+
+ it('should evaluate and dereference promises used as function arguments', function() {
+ scope.greet = function(name) { return 'Hi ' + name + '!'; };
+ scope.name = promise;
+ expect(scope.$eval('greet(name)')).toBe('Hi undefined!');
+
+ scope.$digest();
+ expect(scope.$eval('greet(name)')).toBe('Hi undefined!');
+
+ deferred.resolve('Veronica');
+ expect(scope.$eval('greet(name)')).toBe('Hi undefined!');
+
+ scope.$digest();
+ expect(scope.$eval('greet(name)')).toBe('Hi Veronica!');
+ });
+
+
+ it('should evaluate and dereference promises used as array indexes', function() {
+ scope.childIndex = promise;
+ scope.kids = ['Adam', 'Veronica', 'Elisa'];
+ expect(scope.$eval('kids[childIndex]')).toBe(undefined);
+
+ scope.$digest();
+ expect(scope.$eval('kids[childIndex]')).toBe(undefined);
+
+ deferred.resolve(1);
+ expect(scope.$eval('kids[childIndex]')).toBe(undefined);
+
+ scope.$digest();
+ expect(scope.$eval('kids[childIndex]')).toBe('Veronica');
+ });
+
+
+ it('should evaluate and dereference promises used as keys in bracket notation', function() {
+ scope.childKey = promise;
+ scope.kids = {'a': 'Adam', 'v': 'Veronica', 'e': 'Elisa'};
+
+ expect(scope.$eval('kids[childKey]')).toBe(undefined);
+
+ scope.$digest();
+ expect(scope.$eval('kids[childKey]')).toBe(undefined);
+
+ deferred.resolve('v');
+ expect(scope.$eval('kids[childKey]')).toBe(undefined);
+
+ scope.$digest();
+ expect(scope.$eval('kids[childKey]')).toBe('Veronica');
+ });
+
+
+ it('should not mess with the promise if it was not directly evaluated', function() {
+ scope.obj = {greeting: promise, username: 'hi'};
+ var obj = scope.$eval('obj');
+ expect(obj.username).toEqual('hi');
+ expect(typeof obj.greeting.then).toBe('function');
+ });
+ });
+ });
+
+
describe('assignable', function() {
it('should expose assignment function', inject(function($parse) {
var fn = $parse('a');