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

Commit 1c13a4f

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 1a98c0e commit 1c13a4f

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
@@ -2084,7 +2084,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
20842084
}
20852085

20862086
function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
2087-
var i, ii, linkFn, controller, isolateScope, elementControllers, transcludeFn, $element,
2087+
var linkFn, isolateScope, elementControllers, transcludeFn, $element,
20882088
attrs, removeScopeBindingWatches, removeControllerBindingWatches;
20892089

20902090
if (compileNode === linkNode) {
@@ -2124,38 +2124,27 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
21242124
isolateScope.$on('$destroy', removeScopeBindingWatches);
21252125
}
21262126
}
2127-
if (elementControllers) {
2128-
// Initialize bindToController bindings for new/isolate scopes
2129-
var scopeDirective = newIsolateScopeDirective || newScopeDirective;
2130-
var bindings;
2131-
var controllerForBindings;
2132-
if (scopeDirective && elementControllers[scopeDirective.name]) {
2133-
bindings = scopeDirective.$$bindings.bindToController;
2134-
controller = elementControllers[scopeDirective.name];
2135-
2136-
if (controller && controller.identifier && bindings) {
2137-
controllerForBindings = controller;
2138-
removeControllerBindingWatches =
2139-
initializeDirectiveBindings(scope, attrs, controller.instance,
2140-
bindings, scopeDirective);
2141-
}
2127+
2128+
// Initialize bindToController bindings
2129+
for (var name in elementControllers) {
2130+
var controllerDirective = controllerDirectives[name];
2131+
var controller = elementControllers[name];
2132+
var bindings = controllerDirective.$$bindings.bindToController;
2133+
2134+
if (controller.identifier && bindings) {
2135+
removeControllerBindingWatches =
2136+
initializeDirectiveBindings(scope, attrs, controller.instance, bindings, controllerDirective);
21422137
}
2143-
for (i in elementControllers) {
2144-
controller = elementControllers[i];
2145-
var controllerResult = controller();
2146-
2147-
if (controllerResult !== controller.instance) {
2148-
// If the controller constructor has a return value, overwrite the instance
2149-
// from setupControllers and update the element data
2150-
controller.instance = controllerResult;
2151-
$element.data('$' + i + 'Controller', controllerResult);
2152-
if (controller === controllerForBindings) {
2153-
// Remove and re-install bindToController bindings
2154-
removeControllerBindingWatches && removeControllerBindingWatches();
2155-
removeControllerBindingWatches =
2156-
initializeDirectiveBindings(scope, attrs, controllerResult, bindings, scopeDirective);
2157-
}
2158-
}
2138+
2139+
var controllerResult = controller();
2140+
if (controllerResult !== controller.instance) {
2141+
// If the controller constructor has a return value, overwrite the instance
2142+
// from setupControllers
2143+
controller.instance = controllerResult;
2144+
$element.data('$' + controllerDirective.name + 'Controller', controllerResult);
2145+
removeControllerBindingWatches && removeControllerBindingWatches();
2146+
removeControllerBindingWatches =
2147+
initializeDirectiveBindings(scope, attrs, controller.instance, bindings, controllerDirective);
21592148
}
21602149
}
21612150

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)