diff --git a/src/ng/compile.js b/src/ng/compile.js index 1205736750f4..2f03b67896c3 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -2085,7 +2085,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { } function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) { - var linkFn, isolateScope, elementControllers, transcludeFn, $element, + var linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element, attrs, removeScopeBindingWatches, removeControllerBindingWatches; if (compileNode === linkNode) { @@ -2096,8 +2096,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { attrs = new Attributes($element, templateAttrs); } + controllerScope = scope; if (newIsolateScopeDirective) { isolateScope = scope.$new(true); + } else if (newScopeDirective) { + controllerScope = scope.$parent; } if (boundTranscludeFn) { @@ -2134,7 +2137,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { if (controller.identifier && bindings) { removeControllerBindingWatches = - initializeDirectiveBindings(scope, attrs, controller.instance, bindings, controllerDirective); + initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective); } var controllerResult = controller(); @@ -2145,7 +2148,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { $element.data('$' + controllerDirective.name + 'Controller', controllerResult); removeControllerBindingWatches && removeControllerBindingWatches(); removeControllerBindingWatches = - initializeDirectiveBindings(scope, attrs, controller.instance, bindings, controllerDirective); + initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective); } } diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index aeaa924ac4f9..4e1abbc086dc 100755 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -4591,6 +4591,124 @@ describe('$compile', function() { }); + it('should evaluate against the correct scope, when using `bindToController` (new scope)', + function() { + module(function($compileProvider, $controllerProvider) { + $controllerProvider.register({ + 'ParentCtrl': function() { + this.value1 = 'parent1'; + this.value2 = 'parent2'; + this.value3 = function() { return 'parent3'; }; + }, + 'ChildCtrl': function() { + this.value1 = 'child1'; + this.value2 = 'child2'; + this.value3 = function() { return 'child3'; }; + } + }); + + $compileProvider.directive('child', valueFn({ + scope: true, + controller: 'ChildCtrl as ctrl', + bindToController: { + fromParent1: '@', + fromParent2: '=', + fromParent3: '&' + }, + template: '' + })); + }); + + inject(function($compile, $rootScope) { + element = $compile( + '<div ng-controller="ParentCtrl as ctrl">' + + '<child ' + + 'from-parent-1="{{ ctrl.value1 }}" ' + + 'from-parent-2="ctrl.value2" ' + + 'from-parent-3="ctrl.value3">' + + '</child>' + + '</div>')($rootScope); + $rootScope.$digest(); + + var parentCtrl = element.controller('ngController'); + var childCtrl = element.find('child').controller('child'); + + expect(childCtrl.fromParent1).toBe(parentCtrl.value1); + expect(childCtrl.fromParent1).not.toBe(childCtrl.value1); + expect(childCtrl.fromParent2).toBe(parentCtrl.value2); + expect(childCtrl.fromParent2).not.toBe(childCtrl.value2); + expect(childCtrl.fromParent3()()).toBe(parentCtrl.value3()); + expect(childCtrl.fromParent3()()).not.toBe(childCtrl.value3()); + + childCtrl.fromParent2 = 'modified'; + $rootScope.$digest(); + + expect(parentCtrl.value2).toBe('modified'); + expect(childCtrl.value2).toBe('child2'); + }); + } + ); + + + it('should evaluate against the correct scope, when using `bindToController` (new iso scope)', + function() { + module(function($compileProvider, $controllerProvider) { + $controllerProvider.register({ + 'ParentCtrl': function() { + this.value1 = 'parent1'; + this.value2 = 'parent2'; + this.value3 = function() { return 'parent3'; }; + }, + 'ChildCtrl': function() { + this.value1 = 'child1'; + this.value2 = 'child2'; + this.value3 = function() { return 'child3'; }; + } + }); + + $compileProvider.directive('child', valueFn({ + scope: {}, + controller: 'ChildCtrl as ctrl', + bindToController: { + fromParent1: '@', + fromParent2: '=', + fromParent3: '&' + }, + template: '' + })); + }); + + inject(function($compile, $rootScope) { + element = $compile( + '<div ng-controller="ParentCtrl as ctrl">' + + '<child ' + + 'from-parent-1="{{ ctrl.value1 }}" ' + + 'from-parent-2="ctrl.value2" ' + + 'from-parent-3="ctrl.value3">' + + '</child>' + + '</div>')($rootScope); + $rootScope.$digest(); + + var parentCtrl = element.controller('ngController'); + var childCtrl = element.find('child').controller('child'); + + expect(childCtrl.fromParent1).toBe(parentCtrl.value1); + expect(childCtrl.fromParent1).not.toBe(childCtrl.value1); + expect(childCtrl.fromParent2).toBe(parentCtrl.value2); + expect(childCtrl.fromParent2).not.toBe(childCtrl.value2); + expect(childCtrl.fromParent3()()).toBe(parentCtrl.value3()); + expect(childCtrl.fromParent3()()).not.toBe(childCtrl.value3()); + + childCtrl.fromParent2 = 'modified'; + $rootScope.$digest(); + + expect(parentCtrl.value2).toBe('modified'); + expect(childCtrl.value2).toBe('child2'); + }); + } + ); + + it('should put controller in scope when controller identifier present but not using controllerAs', function() { var controllerCalled = false; var myCtrl;