diff --git a/src/ng/compile.js b/src/ng/compile.js index ea25e0c2e672..bb74cf04e635 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -339,6 +339,11 @@ * One-way binding is useful if you do not plan to propagate changes to your isolated scope bindings * back to the parent. However, it does not make this completely impossible. * + * By default, the {@link ng.$rootScope.Scope#$watch `$watch`} + * method is used for tracking changes, and the equality check is based on object identity. + * It's also possible to watch the evaluated value shallowly with + * {@link ng.$rootScope.Scope#$watchCollection `$watchCollection`}: use `<*` or `<*attr` + * * * `&` 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 local name. * Given `` and the isolate scope definition `scope: { @@ -1068,7 +1073,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { var bindingCache = createMap(); function parseIsolateBindings(scope, directiveName, isController) { - var LOCAL_REGEXP = /^([@&<]|=(\*?))(\??)\s*([\w$]*)$/; + var LOCAL_REGEXP = /^([@&]|[=<](\*?))(\??)\s*([\w$]*)$/; var bindings = createMap(); @@ -3626,7 +3631,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { var initialValue = destination[scopeName] = parentGet(scope); initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]); - removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newValue, oldValue) { + removeWatch = scope[definition.collection ? '$watchCollection' : '$watch'](parentGet, function parentValueWatchAction(newValue, oldValue) { if (oldValue === newValue) { if (oldValue === initialValue || (isLiteral && equals(oldValue, initialValue))) { return; diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index ed4b2f97cfc1..d24a90f9744c 100644 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -5148,6 +5148,9 @@ describe('$compile', function() { owOptref: ''); + + expect(componentScope.owColref).toEqual($rootScope.collection); + expect(componentScope.owColrefAlias).toEqual(componentScope.owColref); + expect(componentScope.$owColrefAlias).toEqual(componentScope.owColref); + + $rootScope.query = 'Gab'; + $rootScope.$apply(); + + expect(componentScope.owColref).toEqual([$rootScope.collection[0]]); + expect(componentScope.owColrefAlias).toEqual([$rootScope.collection[0]]); + expect(componentScope.$owColrefAlias).toEqual([$rootScope.collection[0]]); + })); + + it('should not update isolate scope when deep state within origin scope changes', inject(function() { + $rootScope.collection = [{ + name: 'Gabriel', + value: 18 + }, { + name: 'Tony', + value: 91 + }]; + $rootScope.$apply(); + + compile('
'); + + expect(componentScope.owColref).toEqual($rootScope.collection); + expect(componentScope.owColrefAlias).toEqual(componentScope.owColref); + expect(componentScope.$owColrefAlias).toEqual(componentScope.owColref); + + componentScope.owColref = componentScope.owColrefAlias = componentScope.$owColrefAlias = undefined; + $rootScope.collection[0].name = 'Joe'; + $rootScope.$apply(); + + expect(componentScope.owColref).toBeUndefined(); + expect(componentScope.owColrefAlias).toBeUndefined(); + expect(componentScope.$owColrefAlias).toBeUndefined(); + })); + + it('should update isolate scope when origin scope changes', inject(function() { + $rootScope.gab = { + name: 'Gabriel', + value: 18 + }; + $rootScope.tony = { + name: 'Tony', + value: 91 + }; + $rootScope.query = ''; + $rootScope.$apply(); + + compile('
'); + + expect(componentScope.owColref).toEqual([$rootScope.gab, $rootScope.tony]); + expect(componentScope.owColrefAlias).toEqual([$rootScope.gab, $rootScope.tony]); + expect(componentScope.$owColrefAlias).toEqual([$rootScope.gab, $rootScope.tony]); + + $rootScope.query = 'Gab'; + $rootScope.$apply(); + + expect(componentScope.owColref).toEqual([$rootScope.gab]); + expect(componentScope.owColrefAlias).toEqual([$rootScope.gab]); + expect(componentScope.$owColrefAlias).toEqual([$rootScope.gab]); + })); + + it('should update isolate scope when origin literal object content changes', inject(function() { + $rootScope.gab = { + name: 'Gabriel', + value: 18 + }; + $rootScope.tony = { + name: 'Tony', + value: 91 + }; + $rootScope.$apply(); + + compile('
'); + + expect(componentScope.owColref).toEqual([$rootScope.gab, $rootScope.tony]); + expect(componentScope.owColrefAlias).toEqual([$rootScope.gab, $rootScope.tony]); + expect(componentScope.$owColrefAlias).toEqual([$rootScope.gab, $rootScope.tony]); + + $rootScope.tony = { + name: 'Bob', + value: 42 + }; + $rootScope.$apply(); + + expect(componentScope.owColref).toEqual([$rootScope.gab, $rootScope.tony]); + expect(componentScope.owColrefAlias).toEqual([$rootScope.gab, $rootScope.tony]); + expect(componentScope.$owColrefAlias).toEqual([$rootScope.gab, $rootScope.tony]); + })); + }); + describe('executable expression', function() { it('should allow expression execution with locals', inject(function() { compile('
');