diff options
| -rw-r--r-- | src/service/parse.js | 29 | ||||
| -rw-r--r-- | test/service/parseSpec.js | 165 |
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'); |
