diff --git a/src/ng/compile.js b/src/ng/compile.js index bb78452d5776..0969a670f13d 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -164,7 +164,9 @@ * value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected * in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent * scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You - * can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. + * can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If + * you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use + * `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional). * * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. * If no `attr` name is specified then the attribute name is assumed to be the same as the @@ -691,7 +693,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/; function parseIsolateBindings(scope, directiveName) { - var LOCAL_REGEXP = /^\s*([@=&])(\??)\s*(\w*)\s*$/; + var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/; var bindings = {}; @@ -706,9 +708,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { } bindings[scopeName] = { - attrName: match[3] || scopeName, - mode: match[1], - optional: match[2] === '?' + mode: match[1][0], + collection: match[2] === '*', + optional: match[3] === '?', + attrName: match[4] || scopeName }; }); @@ -1890,7 +1893,12 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { return lastValue = parentValue; }; parentValueWatch.$stateful = true; - var unwatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal); + var unwatch; + if (definition.collection) { + unwatch = scope.$watchCollection(attrs[attrName], parentValueWatch); + } else { + unwatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal); + } isolateScope.$on('$destroy', unwatch); break; diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index d2ed91ef0422..0a25b1c72e55 100755 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -3065,6 +3065,8 @@ describe('$compile', function() { optref: '=?', optrefAlias: '=? optref', optreference: '=?', + colref: '=*', + colrefAlias: '=* colref', expr: '&', exprAlias: '&expr' }, @@ -3562,6 +3564,53 @@ describe('$compile', function() { }); + describe('collection object reference', function () { + it('should update isolate scope when origin scope changes', inject(function () { + $rootScope.collection = [{ + name: 'Gabriel', + value: 18 + }, { + name: 'Tony', + value: 91 + }]; + $rootScope.query = ""; + $rootScope.$apply(); + + compile('
'); + + expect(componentScope.colref).toEqual($rootScope.collection); + expect(componentScope.colrefAlias).toEqual(componentScope.colref); + + $rootScope.query = "Gab"; + $rootScope.$apply(); + + expect(componentScope.colref).toEqual([$rootScope.collection[0]]); + expect(componentScope.colrefAlias).toEqual([$rootScope.collection[0]]); + })); + + it('should update origin scope when isolate scope changes', inject(function () { + $rootScope.collection = [{ + name: 'Gabriel', + value: 18 + }, { + name: 'Tony', + value: 91 + }]; + + compile('
'); + + var newItem = { + name: 'Pablo', + value: 10 + }; + componentScope.colref.push(newItem); + componentScope.$apply(); + + expect($rootScope.collection[2]).toEqual(newItem); + })); + }); + + describe('executable expression', function() { it('should allow expression execution with locals', inject(function() { compile('
');