diff --git a/src/ng/compile.js b/src/ng/compile.js index 485d5079b254..862d7d385818 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -3213,14 +3213,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { parentGet = $parse(attrs[attrName]); - destination[scopeName] = parentGet(scope); + var initialValue = destination[scopeName] = parentGet(scope); initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]); removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newValue, oldValue) { - if (newValue === oldValue) { - // If the new and old values are identical then this is the first time the watch has been triggered - // So instead we use the current value on the destination as the old value - oldValue = destination[scopeName]; + if (oldValue === newValue) { + if (oldValue === initialValue) return; + oldValue = initialValue; } recordChanges(scopeName, newValue, oldValue); destination[scopeName] = newValue; diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index d424fb42f580..e4da3d81b035 100755 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -3823,6 +3823,7 @@ describe('$compile', function() { $rootScope.$apply('a = 7'); element = $compile('')($rootScope); + expect(log).toEqual([ { prop: jasmine.objectContaining({currentValue: 7}), @@ -3862,6 +3863,7 @@ describe('$compile', function() { inject(function($compile, $rootScope) { $rootScope.$apply('a = 7'); element = $compile('')($rootScope); + expect(log).toEqual([ { prop: jasmine.objectContaining({currentValue: 7}), @@ -4744,6 +4746,7 @@ describe('$compile', function() { describe('one-way binding', function() { it('should update isolate when the identity of origin changes', inject(function() { compile('
'); + expect(componentScope.owRef).toBeUndefined(); expect(componentScope.owRefAlias).toBe(componentScope.owRef); @@ -4780,6 +4783,7 @@ describe('$compile', function() { it('should update isolate when both change', inject(function() { compile('
'); + $rootScope.name = {mark:123}; componentScope.owRef = 'misko'; @@ -4796,6 +4800,101 @@ describe('$compile', function() { expect(componentScope.owRefAlias).toBe($rootScope.name); })); + describe('initialization', function() { + var component, log; + + beforeEach(function() { + log = []; + angular.module('owComponentTest', []) + .component('owComponent', { + bindings: { input: '<' }, + controller: function() { + component = this; + this.input = 'constructor'; + log.push('constructor'); + + this.$onInit = function() { + this.input = '$onInit'; + log.push('$onInit'); + }; + + this.$onChanges = function(changes) { + if (changes.input) { + log.push(['$onChanges', changes.input]); + } + }; + } + }); + }); + + it('should not update isolate again after $onInit if outer has not changed', function() { + module('owComponentTest'); + inject(function() { + $rootScope.name = 'outer'; + compile(''); + + expect($rootScope.name).toEqual('outer'); + expect(component.input).toEqual('$onInit'); + + $rootScope.$apply(); + + expect($rootScope.name).toEqual('outer'); + expect(component.input).toEqual('$onInit'); + + expect(log).toEqual([ + 'constructor', + ['$onChanges', jasmine.objectContaining({ currentValue: 'outer' })], + '$onInit' + ]); + }); + }); + + it('should update isolate again after $onInit if outer has changed (before initial watchAction call)', function() { + module('owComponentTest'); + inject(function() { + $rootScope.name = 'outer1'; + compile(''); + + expect(component.input).toEqual('$onInit'); + $rootScope.$apply('name = "outer2"'); + + expect($rootScope.name).toEqual('outer2'); + expect(component.input).toEqual('outer2'); + expect(log).toEqual([ + 'constructor', + ['$onChanges', jasmine.objectContaining({ currentValue: 'outer1' })], + '$onInit', + ['$onChanges', jasmine.objectContaining({ currentValue: 'outer2', previousValue: 'outer1' })] + ]); + }); + }); + + it('should update isolate again after $onInit if outer has changed (before initial watchAction call)', function() { + angular.module('owComponentTest') + .directive('changeInput', function() { + return function(scope, elem, attrs) { + scope.name = 'outer2'; + }; + }); + module('owComponentTest'); + inject(function() { + $rootScope.name = 'outer1'; + compile(''); + + expect(component.input).toEqual('$onInit'); + $rootScope.$digest(); + + expect($rootScope.name).toEqual('outer2'); + expect(component.input).toEqual('outer2'); + expect(log).toEqual([ + 'constructor', + ['$onChanges', jasmine.objectContaining({ currentValue: 'outer1' })], + '$onInit', + ['$onChanges', jasmine.objectContaining({ currentValue: 'outer2', previousValue: 'outer1' })] + ]); + }); + }); + }); it('should not break when isolate and origin both change to the same value', inject(function() { $rootScope.name = 'aaa'; @@ -4819,7 +4918,6 @@ describe('$compile', function() { $rootScope.name = {mark:123}; compile('
'); - $rootScope.$apply(); expect($rootScope.name).toEqual({mark:123}); expect(componentScope.owRef).toBe($rootScope.name); expect(componentScope.owRefAlias).toBe($rootScope.name); @@ -4836,7 +4934,6 @@ describe('$compile', function() { $rootScope.obj = {mark:123}; compile('
'); - $rootScope.$apply(); expect($rootScope.obj).toEqual({mark:123}); expect(componentScope.owRef).toBe($rootScope.obj); @@ -4849,6 +4946,7 @@ describe('$compile', function() { it('should not throw on non assignable expressions in the parent', inject(function() { compile('
'); + $rootScope.name = 'world'; $rootScope.$apply(); expect(componentScope.owRef).toBe('hello world'); @@ -4865,7 +4963,7 @@ describe('$compile', function() { it('should not throw when assigning to undefined', inject(function() { compile('
'); - $rootScope.$apply(); + expect(componentScope.owRef).toBeUndefined(); componentScope.owRef = 'ignore me'; @@ -4879,6 +4977,7 @@ describe('$compile', function() { it('should update isolate scope when "<"-bound NaN changes', inject(function() { $rootScope.num = NaN; compile('
'); + var isolateScope = element.isolateScope(); expect(isolateScope.owRef).toBeNaN(); @@ -4917,7 +5016,7 @@ describe('$compile', function() { $rootScope.name = 'georgios'; $rootScope.obj = {name: 'pete'}; compile('
'); - $rootScope.$apply(); + expect(componentScope.owRef).toEqual([{name: 'georgios'}, {name: 'pete'}]); $rootScope.name = 'lucas'; @@ -4931,7 +5030,7 @@ describe('$compile', function() { $rootScope.name = 'georgios'; $rootScope.obj = {name: 'pete'}; compile('
'); - $rootScope.$apply(); + expect(componentScope.owRef).toEqual({name: 'georgios', item: {name: 'pete'}}); $rootScope.name = 'lucas'; @@ -4967,7 +5066,6 @@ describe('$compile', function() { function test(literalString, literalValue) { compile('
'); - $rootScope.$apply(); expect(componentScope.owRef).toBe(literalValue); dealoc(element); } @@ -4976,6 +5074,7 @@ describe('$compile', function() { describe('optional one-way binding', function() { it('should update local when origin changes', inject(function() { compile('
'); + expect(componentScope.owOptref).toBeUndefined(); expect(componentScope.owOptrefAlias).toBe(componentScope.owOptref);