Skip to content

Commit 9037111

Browse files
committed
fix($compile): bindToController should work without controllerAs
Fixes angular#15088
1 parent c729554 commit 9037111

File tree

3 files changed

+106
-230
lines changed

3 files changed

+106
-230
lines changed

docs/content/error/$compile/noident.ngdoc

-71
This file was deleted.

src/ng/compile.js

+8-20
Original file line numberDiff line numberDiff line change
@@ -360,9 +360,7 @@
360360
*
361361
* #### `bindToController`
362362
* This property is used to bind scope properties directly to the controller. It can be either
363-
* `true` or an object hash with the same format as the `scope` property. Additionally, a controller
364-
* alias must be set, either by using `controllerAs: 'myAlias'` or by specifying the alias in the controller
365-
* definition: `controller: 'myCtrl as myAlias'`.
363+
* `true` or an object hash with the same format as the `scope` property.
366364
*
367365
* When an isolate scope is used for a directive (see above), `bindToController: true` will
368366
* allow a component to have its properties bound to the controller, rather than to scope.
@@ -1027,20 +1025,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
10271025
bindings.bindToController =
10281026
parseIsolateBindings(directive.bindToController, directiveName, true);
10291027
}
1030-
if (isObject(bindings.bindToController)) {
1031-
var controller = directive.controller;
1032-
var controllerAs = directive.controllerAs;
1033-
if (!controller) {
1034-
// There is no controller, there may or may not be a controllerAs property
1035-
throw $compileMinErr('noctrl',
1036-
'Cannot bind to controller without directive \'{0}\'s controller.',
1037-
directiveName);
1038-
} else if (!identifierForController(controller, controllerAs)) {
1039-
// There is a controller, but no identifier or controllerAs property
1040-
throw $compileMinErr('noident',
1041-
'Cannot bind to controller without identifier for directive \'{0}\'.',
1042-
directiveName);
1043-
}
1028+
if (bindings.bindToController && !directive.controller) {
1029+
// There is no controller
1030+
throw $compileMinErr('noctrl',
1031+
'Cannot bind to controller without directive \'{0}\'s controller.',
1032+
directiveName);
10441033
}
10451034
return bindings;
10461035
}
@@ -2709,7 +2698,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
27092698
var bindings = controllerDirective.$$bindings.bindToController;
27102699

27112700
if (preAssignBindingsEnabled) {
2712-
if (controller.identifier && bindings) {
2701+
if (bindings) {
27132702
controller.bindingInfo =
27142703
initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
27152704
} else {
@@ -3412,8 +3401,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
34123401
}
34133402

34143403

3415-
// Set up $watches for isolate scope and controller bindings. This process
3416-
// only occurs for isolate scopes and new scopes with controllerAs.
3404+
// Set up $watches for isolate scope and controller bindings.
34173405
function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
34183406
var removeWatchCollection = [];
34193407
var initialChanges = {};

test/ng/compileSpec.js

+98-139
Original file line numberDiff line numberDiff line change
@@ -6126,154 +6126,113 @@ describe('$compile', function() {
61266126
});
61276127

61286128

6129-
it('should throw noident when missing controllerAs directive property', function() {
6130-
module(function($compileProvider) {
6131-
$compileProvider.directive('noIdent', valueFn({
6132-
templateUrl: 'test.html',
6133-
scope: {
6134-
'data': '=dirData',
6135-
'oneway': '<dirData',
6136-
'str': '@dirStr',
6137-
'fn': '&dirFn'
6138-
},
6139-
controller: function() {},
6140-
bindToController: true
6141-
}));
6142-
});
6143-
inject(function($compile, $rootScope) {
6144-
expect(function() {
6145-
$compile('<div no-ident>')($rootScope);
6146-
}).toThrowMinErr('$compile', 'noident',
6147-
'Cannot bind to controller without identifier for directive \'noIdent\'.');
6148-
});
6149-
});
6150-
6151-
6152-
it('should throw noident when missing controller identifier', function() {
6153-
module(function($compileProvider, $controllerProvider) {
6154-
$controllerProvider.register('myCtrl', function() {});
6155-
$compileProvider.directive('noIdent', valueFn({
6156-
templateUrl: 'test.html',
6157-
scope: {
6158-
'data': '=dirData',
6159-
'oneway': '<dirData',
6160-
'str': '@dirStr',
6161-
'fn': '&dirFn'
6162-
},
6129+
describe('should bind to controller via object notation', function() {
6130+
var controllerOptions = [{
6131+
description: 'no controller identifier',
6132+
controller: 'myCtrl'
6133+
}, {
6134+
description: '"Ctrl as ident" syntax',
6135+
controller: 'myCtrl as myCtrl'
6136+
}, {
6137+
description: 'controllerAs setting',
61636138
controller: 'myCtrl',
6164-
bindToController: true
6165-
}));
6166-
});
6167-
inject(function($compile, $rootScope) {
6168-
expect(function() {
6169-
$compile('<div no-ident>')($rootScope);
6170-
}).toThrowMinErr('$compile', 'noident',
6171-
'Cannot bind to controller without identifier for directive \'noIdent\'.');
6172-
});
6173-
});
6139+
controllerAs: 'myCtrl'
6140+
}],
61746141

6142+
scopeOptions = [{
6143+
description: 'isolate scope',
6144+
scope: {}
6145+
}, {
6146+
description: 'new scope',
6147+
scope: true
6148+
}, {
6149+
description: 'no scope',
6150+
scope: false
6151+
}],
61756152

6176-
it('should bind to controller via object notation (isolate scope)', function() {
6177-
var controllerCalled = false;
6178-
module(function($compileProvider, $controllerProvider) {
6179-
$controllerProvider.register('myCtrl', function() {
6180-
this.check = function() {
6181-
expect(this.data).toEqualData({
6182-
'foo': 'bar',
6183-
'baz': 'biz'
6184-
});
6185-
expect(this.oneway).toEqualData({
6186-
'foo': 'bar',
6187-
'baz': 'biz'
6188-
});
6189-
expect(this.str).toBe('Hello, world!');
6190-
expect(this.fn()).toBe('called!');
6191-
};
6192-
controllerCalled = true;
6193-
if (preAssignBindingsEnabled) {
6194-
this.check();
6195-
} else {
6196-
this.$onInit = this.check;
6197-
}
6198-
});
6199-
$compileProvider.directive('fooDir', valueFn({
6200-
templateUrl: 'test.html',
6201-
bindToController: {
6202-
'data': '=dirData',
6203-
'oneway': '<dirData',
6204-
'str': '@dirStr',
6205-
'fn': '&dirFn'
6206-
},
6207-
scope: {},
6208-
controller: 'myCtrl as myCtrl'
6209-
}));
6210-
});
6211-
inject(function($compile, $rootScope, $templateCache) {
6212-
$templateCache.put('test.html', '<p>isolate</p>');
6213-
$rootScope.fn = valueFn('called!');
6214-
$rootScope.whom = 'world';
6215-
$rootScope.remoteData = {
6216-
'foo': 'bar',
6217-
'baz': 'biz'
6218-
};
6219-
element = $compile('<div foo-dir dir-data="remoteData" ' +
6220-
'dir-str="Hello, {{whom}}!" ' +
6221-
'dir-fn="fn()"></div>')($rootScope);
6222-
$rootScope.$digest();
6223-
expect(controllerCalled).toBe(true);
6224-
});
6225-
});
6153+
templateOptions = [{
6154+
description: 'inline template',
6155+
template: '<p>template</p>'
6156+
}, {
6157+
description: 'templateUrl setting',
6158+
templateUrl: 'test.html'
6159+
}, {
6160+
description: 'no template'
6161+
}];
62266162

6163+
forEach(controllerOptions, function(controllerOption) {
6164+
forEach(scopeOptions, function(scopeOption) {
6165+
forEach(templateOptions, function(templateOption) {
6166+
6167+
var description = [],
6168+
ddo = {
6169+
bindToController: {
6170+
'data': '=dirData',
6171+
'oneway': '<dirData',
6172+
'str': '@dirStr',
6173+
'fn': '&dirFn'
6174+
}
6175+
};
62276176

6228-
it('should bind to controller via object notation (new scope)', function() {
6229-
var controllerCalled = false;
6230-
module(function($compileProvider, $controllerProvider) {
6231-
$controllerProvider.register('myCtrl', function() {
6232-
this.check = function() {
6233-
expect(this.data).toEqualData({
6234-
'foo': 'bar',
6235-
'baz': 'biz'
6177+
forEach([controllerOption, scopeOption, templateOption], function(option) {
6178+
description.push(option.description);
6179+
delete option.description;
6180+
extend(ddo, option);
62366181
});
6237-
expect(this.data).toEqualData({
6238-
'foo': 'bar',
6239-
'baz': 'biz'
6182+
6183+
it('(' + description.join(', ') + ')', function() {
6184+
var controllerCalled = false;
6185+
module(function($compileProvider, $controllerProvider) {
6186+
$controllerProvider.register('myCtrl', function() {
6187+
this.check = function() {
6188+
expect(this.data).toEqualData({
6189+
'foo': 'bar',
6190+
'baz': 'biz'
6191+
});
6192+
expect(this.oneway).toEqualData({
6193+
'foo': 'bar',
6194+
'baz': 'biz'
6195+
});
6196+
expect(this.str).toBe('Hello, world!');
6197+
expect(this.fn()).toBe('called!');
6198+
};
6199+
controllerCalled = true;
6200+
if (preAssignBindingsEnabled) {
6201+
this.check();
6202+
} else {
6203+
this.$onInit = this.check;
6204+
}
6205+
});
6206+
$compileProvider.directive('fooDir', valueFn(ddo));
6207+
});
6208+
inject(function($compile, $rootScope, $templateCache) {
6209+
$templateCache.put('test.html', '<p>template</p>');
6210+
$rootScope.fn = valueFn('called!');
6211+
$rootScope.whom = 'world';
6212+
$rootScope.remoteData = {
6213+
'foo': 'bar',
6214+
'baz': 'biz'
6215+
};
6216+
element = $compile('<div foo-dir dir-data="remoteData" ' +
6217+
'dir-str="Hello, {{whom}}!" ' +
6218+
'dir-fn="fn()"></div>')($rootScope);
6219+
$rootScope.$digest();
6220+
expect(controllerCalled).toBe(true);
6221+
if (ddo.controllerAs || ddo.controller.indexOf(' as ') !== -1) {
6222+
if (ddo.scope) {
6223+
expect($rootScope.myCtrl).toBeUndefined();
6224+
} else {
6225+
// The controller identifier was added to the containing scope.
6226+
expect($rootScope.myCtrl).not.toBeUndefined();
6227+
}
6228+
}
6229+
});
62406230
});
6241-
expect(this.str).toBe('Hello, world!');
6242-
expect(this.fn()).toBe('called!');
6243-
};
6244-
controllerCalled = true;
6245-
if (preAssignBindingsEnabled) {
6246-
this.check();
6247-
} else {
6248-
this.$onInit = this.check;
6249-
}
6231+
6232+
});
62506233
});
6251-
$compileProvider.directive('fooDir', valueFn({
6252-
templateUrl: 'test.html',
6253-
bindToController: {
6254-
'data': '=dirData',
6255-
'oneway': '<dirData',
6256-
'str': '@dirStr',
6257-
'fn': '&dirFn'
6258-
},
6259-
scope: true,
6260-
controller: 'myCtrl as myCtrl'
6261-
}));
6262-
});
6263-
inject(function($compile, $rootScope, $templateCache) {
6264-
$templateCache.put('test.html', '<p>isolate</p>');
6265-
$rootScope.fn = valueFn('called!');
6266-
$rootScope.whom = 'world';
6267-
$rootScope.remoteData = {
6268-
'foo': 'bar',
6269-
'baz': 'biz'
6270-
};
6271-
element = $compile('<div foo-dir dir-data="remoteData" ' +
6272-
'dir-str="Hello, {{whom}}!" ' +
6273-
'dir-fn="fn()"></div>')($rootScope);
6274-
$rootScope.$digest();
6275-
expect(controllerCalled).toBe(true);
62766234
});
6235+
62776236
});
62786237

62796238

0 commit comments

Comments
 (0)