Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 7503441

Browse files
jtorbickipetebacondarwin
authored andcommitted
fix($compile): bind all directive controllers correctly when using bindToController
Previously only the first directive's controller would be bound correctly. Closes #11343 Closes #11345
1 parent 74da034 commit 7503441

File tree

2 files changed

+198
-32
lines changed

2 files changed

+198
-32
lines changed

src/ng/compile.js

+21-32
Original file line numberDiff line numberDiff line change
@@ -1977,7 +1977,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
19771977
}
19781978

19791979
function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
1980-
var i, ii, linkFn, controller, isolateScope, elementControllers, transcludeFn, $element,
1980+
var linkFn, isolateScope, elementControllers, transcludeFn, $element,
19811981
attrs, removeScopeBindingWatches, removeControllerBindingWatches;
19821982

19831983
if (compileNode === linkNode) {
@@ -2017,38 +2017,27 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
20172017
isolateScope.$on('$destroy', removeScopeBindingWatches);
20182018
}
20192019
}
2020-
if (elementControllers) {
2021-
// Initialize bindToController bindings for new/isolate scopes
2022-
var scopeDirective = newIsolateScopeDirective || newScopeDirective;
2023-
var bindings;
2024-
var controllerForBindings;
2025-
if (scopeDirective && elementControllers[scopeDirective.name]) {
2026-
bindings = scopeDirective.$$bindings.bindToController;
2027-
controller = elementControllers[scopeDirective.name];
2028-
2029-
if (controller && controller.identifier && bindings) {
2030-
controllerForBindings = controller;
2031-
removeControllerBindingWatches =
2032-
initializeDirectiveBindings(scope, attrs, controller.instance,
2033-
bindings, scopeDirective);
2034-
}
2020+
2021+
// Initialize bindToController bindings
2022+
for (var name in elementControllers) {
2023+
var controllerDirective = controllerDirectives[name];
2024+
var controller = elementControllers[name];
2025+
var bindings = controllerDirective.$$bindings.bindToController;
2026+
2027+
if (controller.identifier && bindings) {
2028+
removeControllerBindingWatches =
2029+
initializeDirectiveBindings(scope, attrs, controller.instance, bindings, controllerDirective);
20352030
}
2036-
for (i in elementControllers) {
2037-
controller = elementControllers[i];
2038-
var controllerResult = controller();
2039-
2040-
if (controllerResult !== controller.instance) {
2041-
// If the controller constructor has a return value, overwrite the instance
2042-
// from setupControllers and update the element data
2043-
controller.instance = controllerResult;
2044-
$element.data('$' + i + 'Controller', controllerResult);
2045-
if (controller === controllerForBindings) {
2046-
// Remove and re-install bindToController bindings
2047-
removeControllerBindingWatches && removeControllerBindingWatches();
2048-
removeControllerBindingWatches =
2049-
initializeDirectiveBindings(scope, attrs, controllerResult, bindings, scopeDirective);
2050-
}
2051-
}
2031+
2032+
var controllerResult = controller();
2033+
if (controllerResult !== controller.instance) {
2034+
// If the controller constructor has a return value, overwrite the instance
2035+
// from setupControllers
2036+
controller.instance = controllerResult;
2037+
$element.data('$' + controllerDirective.name + 'Controller', controllerResult);
2038+
removeControllerBindingWatches && removeControllerBindingWatches();
2039+
removeControllerBindingWatches =
2040+
initializeDirectiveBindings(scope, attrs, controller.instance, bindings, controllerDirective);
20522041
}
20532042
}
20542043

test/ng/compileSpec.js

+177
Original file line numberDiff line numberDiff line change
@@ -4414,6 +4414,183 @@ describe('$compile', function() {
44144414
});
44154415

44164416

4417+
it('should bind to multiple directives controllers via object notation (no scope)', function() {
4418+
var controller1Called = false;
4419+
var controller2Called = false;
4420+
module(function($compileProvider, $controllerProvider) {
4421+
$compileProvider.directive('foo', valueFn({
4422+
bindToController: {
4423+
'data': '=fooData',
4424+
'str': '@fooStr',
4425+
'fn': '&fooFn'
4426+
},
4427+
controllerAs: 'fooCtrl',
4428+
controller: function() {
4429+
expect(this.data).toEqualData({'foo': 'bar', 'baz': 'biz'});
4430+
expect(this.str).toBe('Hello, world!');
4431+
expect(this.fn()).toBe('called!');
4432+
controller1Called = true;
4433+
}
4434+
}));
4435+
$compileProvider.directive('bar', valueFn({
4436+
bindToController: {
4437+
'data': '=barData',
4438+
'str': '@barStr',
4439+
'fn': '&barFn'
4440+
},
4441+
controllerAs: 'barCtrl',
4442+
controller: function() {
4443+
expect(this.data).toEqualData({'foo2': 'bar2', 'baz2': 'biz2'});
4444+
expect(this.str).toBe('Hello, second world!');
4445+
expect(this.fn()).toBe('second called!');
4446+
controller2Called = true;
4447+
}
4448+
}));
4449+
});
4450+
inject(function($compile, $rootScope) {
4451+
$rootScope.fn = valueFn('called!');
4452+
$rootScope.string = 'world';
4453+
$rootScope.data = {'foo': 'bar','baz': 'biz'};
4454+
$rootScope.fn2 = valueFn('second called!');
4455+
$rootScope.string2 = 'second world';
4456+
$rootScope.data2 = {'foo2': 'bar2', 'baz2': 'biz2'};
4457+
element = $compile(
4458+
'<div ' +
4459+
'foo ' +
4460+
'foo-data="data" ' +
4461+
'foo-str="Hello, {{string}}!" ' +
4462+
'foo-fn="fn()" ' +
4463+
'bar ' +
4464+
'bar-data="data2" ' +
4465+
'bar-str="Hello, {{string2}}!" ' +
4466+
'bar-fn="fn2()" > ' +
4467+
'</div>')($rootScope);
4468+
$rootScope.$digest();
4469+
expect(controller1Called).toBe(true);
4470+
expect(controller2Called).toBe(true);
4471+
});
4472+
});
4473+
4474+
4475+
it('should bind to multiple directives controllers via object notation (new iso scope)', function() {
4476+
var controller1Called = false;
4477+
var controller2Called = false;
4478+
module(function($compileProvider, $controllerProvider) {
4479+
$compileProvider.directive('foo', valueFn({
4480+
bindToController: {
4481+
'data': '=fooData',
4482+
'str': '@fooStr',
4483+
'fn': '&fooFn'
4484+
},
4485+
scope: true,
4486+
controllerAs: 'fooCtrl',
4487+
controller: function() {
4488+
expect(this.data).toEqualData({'foo': 'bar', 'baz': 'biz'});
4489+
expect(this.str).toBe('Hello, world!');
4490+
expect(this.fn()).toBe('called!');
4491+
controller1Called = true;
4492+
}
4493+
}));
4494+
$compileProvider.directive('bar', valueFn({
4495+
bindToController: {
4496+
'data': '=barData',
4497+
'str': '@barStr',
4498+
'fn': '&barFn'
4499+
},
4500+
controllerAs: 'barCtrl',
4501+
controller: function() {
4502+
expect(this.data).toEqualData({'foo2': 'bar2', 'baz2': 'biz2'});
4503+
expect(this.str).toBe('Hello, second world!');
4504+
expect(this.fn()).toBe('second called!');
4505+
controller2Called = true;
4506+
}
4507+
}));
4508+
});
4509+
inject(function($compile, $rootScope) {
4510+
$rootScope.fn = valueFn('called!');
4511+
$rootScope.string = 'world';
4512+
$rootScope.data = {'foo': 'bar','baz': 'biz'};
4513+
$rootScope.fn2 = valueFn('second called!');
4514+
$rootScope.string2 = 'second world';
4515+
$rootScope.data2 = {'foo2': 'bar2', 'baz2': 'biz2'};
4516+
element = $compile(
4517+
'<div ' +
4518+
'foo ' +
4519+
'foo-data="data" ' +
4520+
'foo-str="Hello, {{string}}!" ' +
4521+
'foo-fn="fn()" ' +
4522+
'bar ' +
4523+
'bar-data="data2" ' +
4524+
'bar-str="Hello, {{string2}}!" ' +
4525+
'bar-fn="fn2()" > ' +
4526+
'</div>')($rootScope);
4527+
$rootScope.$digest();
4528+
expect(controller1Called).toBe(true);
4529+
expect(controller2Called).toBe(true);
4530+
});
4531+
});
4532+
4533+
4534+
it('should bind to multiple directives controllers via object notation (new scope)', function() {
4535+
var controller1Called = false;
4536+
var controller2Called = false;
4537+
module(function($compileProvider, $controllerProvider) {
4538+
$compileProvider.directive('foo', valueFn({
4539+
bindToController: {
4540+
'data': '=fooData',
4541+
'str': '@fooStr',
4542+
'fn': '&fooFn'
4543+
},
4544+
scope: true,
4545+
controllerAs: 'fooCtrl',
4546+
controller: function() {
4547+
expect(this.data).toEqualData({'foo': 'bar', 'baz': 'biz'});
4548+
expect(this.str).toBe('Hello, world!');
4549+
expect(this.fn()).toBe('called!');
4550+
controller1Called = true;
4551+
}
4552+
}));
4553+
$compileProvider.directive('bar', valueFn({
4554+
bindToController: {
4555+
'data': '=barData',
4556+
'str': '@barStr',
4557+
'fn': '&barFn'
4558+
},
4559+
scope: true,
4560+
controllerAs: 'barCtrl',
4561+
controller: function() {
4562+
expect(this.data).toEqualData({'foo2': 'bar2', 'baz2': 'biz2'});
4563+
expect(this.str).toBe('Hello, second world!');
4564+
expect(this.fn()).toBe('second called!');
4565+
controller2Called = true;
4566+
}
4567+
}));
4568+
});
4569+
inject(function($compile, $rootScope) {
4570+
$rootScope.fn = valueFn('called!');
4571+
$rootScope.string = 'world';
4572+
$rootScope.data = {'foo': 'bar','baz': 'biz'};
4573+
$rootScope.fn2 = valueFn('second called!');
4574+
$rootScope.string2 = 'second world';
4575+
$rootScope.data2 = {'foo2': 'bar2', 'baz2': 'biz2'};
4576+
element = $compile(
4577+
'<div ' +
4578+
'foo ' +
4579+
'foo-data="data" ' +
4580+
'foo-str="Hello, {{string}}!" ' +
4581+
'foo-fn="fn()" ' +
4582+
'bar ' +
4583+
'bar-data="data2" ' +
4584+
'bar-str="Hello, {{string2}}!" ' +
4585+
'bar-fn="fn2()" > ' +
4586+
'</div>')($rootScope);
4587+
$rootScope.$digest();
4588+
expect(controller1Called).toBe(true);
4589+
expect(controller2Called).toBe(true);
4590+
});
4591+
});
4592+
4593+
44174594
it('should put controller in scope when controller identifier present but not using controllerAs', function() {
44184595
var controllerCalled = false;
44194596
var myCtrl;

0 commit comments

Comments
 (0)