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

Commit 9449893

Browse files
thorn0petebacondarwin
authored andcommitted
fix($compile): bindToController should work without controllerAs
Fixes #15088 Closes #15110
1 parent 07e1ba3 commit 9449893

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
@@ -361,9 +361,7 @@
361361
*
362362
* #### `bindToController`
363363
* This property is used to bind scope properties directly to the controller. It can be either
364-
* `true` or an object hash with the same format as the `scope` property. Additionally, a controller
365-
* alias must be set, either by using `controllerAs: 'myAlias'` or by specifying the alias in the controller
366-
* definition: `controller: 'myCtrl as myAlias'`.
364+
* `true` or an object hash with the same format as the `scope` property.
367365
*
368366
* When an isolate scope is used for a directive (see above), `bindToController: true` will
369367
* allow a component to have its properties bound to the controller, rather than to scope.
@@ -1028,20 +1026,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
10281026
bindings.bindToController =
10291027
parseIsolateBindings(directive.bindToController, directiveName, true);
10301028
}
1031-
if (isObject(bindings.bindToController)) {
1032-
var controller = directive.controller;
1033-
var controllerAs = directive.controllerAs;
1034-
if (!controller) {
1035-
// There is no controller, there may or may not be a controllerAs property
1036-
throw $compileMinErr('noctrl',
1037-
'Cannot bind to controller without directive \'{0}\'s controller.',
1038-
directiveName);
1039-
} else if (!identifierForController(controller, controllerAs)) {
1040-
// There is a controller, but no identifier or controllerAs property
1041-
throw $compileMinErr('noident',
1042-
'Cannot bind to controller without identifier for directive \'{0}\'.',
1043-
directiveName);
1044-
}
1029+
if (bindings.bindToController && !directive.controller) {
1030+
// There is no controller
1031+
throw $compileMinErr('noctrl',
1032+
'Cannot bind to controller without directive \'{0}\'s controller.',
1033+
directiveName);
10451034
}
10461035
return bindings;
10471036
}
@@ -2697,7 +2686,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
26972686
var bindings = controllerDirective.$$bindings.bindToController;
26982687

26992688
if (preAssignBindingsEnabled) {
2700-
if (controller.identifier && bindings) {
2689+
if (bindings) {
27012690
controller.bindingInfo =
27022691
initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
27032692
} else {
@@ -3394,8 +3383,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
33943383
}
33953384

33963385

3397-
// Set up $watches for isolate scope and controller bindings. This process
3398-
// only occurs for isolate scopes and new scopes with controllerAs.
3386+
// Set up $watches for isolate scope and controller bindings.
33993387
function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
34003388
var removeWatchCollection = [];
34013389
var initialChanges = {};

test/ng/compileSpec.js

+98-139
Original file line numberDiff line numberDiff line change
@@ -6157,154 +6157,113 @@ describe('$compile', function() {
61576157
});
61586158

61596159

6160-
it('should throw noident when missing controllerAs directive property', function() {
6161-
module(function($compileProvider) {
6162-
$compileProvider.directive('noIdent', valueFn({
6163-
templateUrl: 'test.html',
6164-
scope: {
6165-
'data': '=dirData',
6166-
'oneway': '<dirData',
6167-
'str': '@dirStr',
6168-
'fn': '&dirFn'
6169-
},
6170-
controller: function() {},
6171-
bindToController: true
6172-
}));
6173-
});
6174-
inject(function($compile, $rootScope) {
6175-
expect(function() {
6176-
$compile('<div no-ident>')($rootScope);
6177-
}).toThrowMinErr('$compile', 'noident',
6178-
'Cannot bind to controller without identifier for directive \'noIdent\'.');
6179-
});
6180-
});
6181-
6182-
6183-
it('should throw noident when missing controller identifier', function() {
6184-
module(function($compileProvider, $controllerProvider) {
6185-
$controllerProvider.register('myCtrl', function() {});
6186-
$compileProvider.directive('noIdent', valueFn({
6187-
templateUrl: 'test.html',
6188-
scope: {
6189-
'data': '=dirData',
6190-
'oneway': '<dirData',
6191-
'str': '@dirStr',
6192-
'fn': '&dirFn'
6193-
},
6160+
describe('should bind to controller via object notation', function() {
6161+
var controllerOptions = [{
6162+
description: 'no controller identifier',
6163+
controller: 'myCtrl'
6164+
}, {
6165+
description: '"Ctrl as ident" syntax',
6166+
controller: 'myCtrl as myCtrl'
6167+
}, {
6168+
description: 'controllerAs setting',
61946169
controller: 'myCtrl',
6195-
bindToController: true
6196-
}));
6197-
});
6198-
inject(function($compile, $rootScope) {
6199-
expect(function() {
6200-
$compile('<div no-ident>')($rootScope);
6201-
}).toThrowMinErr('$compile', 'noident',
6202-
'Cannot bind to controller without identifier for directive \'noIdent\'.');
6203-
});
6204-
});
6170+
controllerAs: 'myCtrl'
6171+
}],
62056172

6173+
scopeOptions = [{
6174+
description: 'isolate scope',
6175+
scope: {}
6176+
}, {
6177+
description: 'new scope',
6178+
scope: true
6179+
}, {
6180+
description: 'no scope',
6181+
scope: false
6182+
}],
62066183

6207-
it('should bind to controller via object notation (isolate scope)', function() {
6208-
var controllerCalled = false;
6209-
module(function($compileProvider, $controllerProvider) {
6210-
$controllerProvider.register('myCtrl', function() {
6211-
this.check = function() {
6212-
expect(this.data).toEqualData({
6213-
'foo': 'bar',
6214-
'baz': 'biz'
6215-
});
6216-
expect(this.oneway).toEqualData({
6217-
'foo': 'bar',
6218-
'baz': 'biz'
6219-
});
6220-
expect(this.str).toBe('Hello, world!');
6221-
expect(this.fn()).toBe('called!');
6222-
};
6223-
controllerCalled = true;
6224-
if (preAssignBindingsEnabled) {
6225-
this.check();
6226-
} else {
6227-
this.$onInit = this.check;
6228-
}
6229-
});
6230-
$compileProvider.directive('fooDir', valueFn({
6231-
templateUrl: 'test.html',
6232-
bindToController: {
6233-
'data': '=dirData',
6234-
'oneway': '<dirData',
6235-
'str': '@dirStr',
6236-
'fn': '&dirFn'
6237-
},
6238-
scope: {},
6239-
controller: 'myCtrl as myCtrl'
6240-
}));
6241-
});
6242-
inject(function($compile, $rootScope, $templateCache) {
6243-
$templateCache.put('test.html', '<p>isolate</p>');
6244-
$rootScope.fn = valueFn('called!');
6245-
$rootScope.whom = 'world';
6246-
$rootScope.remoteData = {
6247-
'foo': 'bar',
6248-
'baz': 'biz'
6249-
};
6250-
element = $compile('<div foo-dir dir-data="remoteData" ' +
6251-
'dir-str="Hello, {{whom}}!" ' +
6252-
'dir-fn="fn()"></div>')($rootScope);
6253-
$rootScope.$digest();
6254-
expect(controllerCalled).toBe(true);
6255-
});
6256-
});
6184+
templateOptions = [{
6185+
description: 'inline template',
6186+
template: '<p>template</p>'
6187+
}, {
6188+
description: 'templateUrl setting',
6189+
templateUrl: 'test.html'
6190+
}, {
6191+
description: 'no template'
6192+
}];
62576193

6194+
forEach(controllerOptions, function(controllerOption) {
6195+
forEach(scopeOptions, function(scopeOption) {
6196+
forEach(templateOptions, function(templateOption) {
6197+
6198+
var description = [],
6199+
ddo = {
6200+
bindToController: {
6201+
'data': '=dirData',
6202+
'oneway': '<dirData',
6203+
'str': '@dirStr',
6204+
'fn': '&dirFn'
6205+
}
6206+
};
62586207

6259-
it('should bind to controller via object notation (new scope)', function() {
6260-
var controllerCalled = false;
6261-
module(function($compileProvider, $controllerProvider) {
6262-
$controllerProvider.register('myCtrl', function() {
6263-
this.check = function() {
6264-
expect(this.data).toEqualData({
6265-
'foo': 'bar',
6266-
'baz': 'biz'
6208+
forEach([controllerOption, scopeOption, templateOption], function(option) {
6209+
description.push(option.description);
6210+
delete option.description;
6211+
extend(ddo, option);
62676212
});
6268-
expect(this.data).toEqualData({
6269-
'foo': 'bar',
6270-
'baz': 'biz'
6213+
6214+
it('(' + description.join(', ') + ')', function() {
6215+
var controllerCalled = false;
6216+
module(function($compileProvider, $controllerProvider) {
6217+
$controllerProvider.register('myCtrl', function() {
6218+
this.check = function() {
6219+
expect(this.data).toEqualData({
6220+
'foo': 'bar',
6221+
'baz': 'biz'
6222+
});
6223+
expect(this.oneway).toEqualData({
6224+
'foo': 'bar',
6225+
'baz': 'biz'
6226+
});
6227+
expect(this.str).toBe('Hello, world!');
6228+
expect(this.fn()).toBe('called!');
6229+
};
6230+
controllerCalled = true;
6231+
if (preAssignBindingsEnabled) {
6232+
this.check();
6233+
} else {
6234+
this.$onInit = this.check;
6235+
}
6236+
});
6237+
$compileProvider.directive('fooDir', valueFn(ddo));
6238+
});
6239+
inject(function($compile, $rootScope, $templateCache) {
6240+
$templateCache.put('test.html', '<p>template</p>');
6241+
$rootScope.fn = valueFn('called!');
6242+
$rootScope.whom = 'world';
6243+
$rootScope.remoteData = {
6244+
'foo': 'bar',
6245+
'baz': 'biz'
6246+
};
6247+
element = $compile('<div foo-dir dir-data="remoteData" ' +
6248+
'dir-str="Hello, {{whom}}!" ' +
6249+
'dir-fn="fn()"></div>')($rootScope);
6250+
$rootScope.$digest();
6251+
expect(controllerCalled).toBe(true);
6252+
if (ddo.controllerAs || ddo.controller.indexOf(' as ') !== -1) {
6253+
if (ddo.scope) {
6254+
expect($rootScope.myCtrl).toBeUndefined();
6255+
} else {
6256+
// The controller identifier was added to the containing scope.
6257+
expect($rootScope.myCtrl).toBeDefined();
6258+
}
6259+
}
6260+
});
62716261
});
6272-
expect(this.str).toBe('Hello, world!');
6273-
expect(this.fn()).toBe('called!');
6274-
};
6275-
controllerCalled = true;
6276-
if (preAssignBindingsEnabled) {
6277-
this.check();
6278-
} else {
6279-
this.$onInit = this.check;
6280-
}
6262+
6263+
});
62816264
});
6282-
$compileProvider.directive('fooDir', valueFn({
6283-
templateUrl: 'test.html',
6284-
bindToController: {
6285-
'data': '=dirData',
6286-
'oneway': '<dirData',
6287-
'str': '@dirStr',
6288-
'fn': '&dirFn'
6289-
},
6290-
scope: true,
6291-
controller: 'myCtrl as myCtrl'
6292-
}));
6293-
});
6294-
inject(function($compile, $rootScope, $templateCache) {
6295-
$templateCache.put('test.html', '<p>isolate</p>');
6296-
$rootScope.fn = valueFn('called!');
6297-
$rootScope.whom = 'world';
6298-
$rootScope.remoteData = {
6299-
'foo': 'bar',
6300-
'baz': 'biz'
6301-
};
6302-
element = $compile('<div foo-dir dir-data="remoteData" ' +
6303-
'dir-str="Hello, {{whom}}!" ' +
6304-
'dir-fn="fn()"></div>')($rootScope);
6305-
$rootScope.$digest();
6306-
expect(controllerCalled).toBe(true);
63076265
});
6266+
63086267
});
63096268

63106269

0 commit comments

Comments
 (0)