From 9037111c856e275c7f59191f5f71972fa408311c Mon Sep 17 00:00:00 2001
From: thorn0
Date: Thu, 8 Sep 2016 17:54:13 +0300
Subject: [PATCH] fix($compile): `bindToController` should work without
`controllerAs`
Fixes #15088
---
docs/content/error/$compile/noident.ngdoc | 71 -------
src/ng/compile.js | 28 +--
test/ng/compileSpec.js | 237 +++++++++-------------
3 files changed, 106 insertions(+), 230 deletions(-)
delete mode 100644 docs/content/error/$compile/noident.ngdoc
diff --git a/docs/content/error/$compile/noident.ngdoc b/docs/content/error/$compile/noident.ngdoc
deleted file mode 100644
index 9770a94585e1..000000000000
--- a/docs/content/error/$compile/noident.ngdoc
+++ /dev/null
@@ -1,71 +0,0 @@
-@ngdoc error
-@name $compile:noident
-@fullName Controller identifier is required.
-@description
-
-When using the `bindToController` feature of AngularJS, a directive is required
-to have a Controller identifier, which is initialized in scope with the value of
-the controller instance. This can be supplied using the "controllerAs" property
-of the directive object, or alternatively by adding " as IDENTIFIER" to the controller
-name.
-
-For example, the following directives are valid:
-
-```js
-// OKAY, because controller is a string with an identifier component.
-directive("okay", function() {
- return {
- bindToController: true,
- controller: "myCtrl as $ctrl",
- scope: {
- text: "@text"
- }
- };
-});
-
-
-// OKAY, because the directive uses the controllerAs property to override
-// the controller identifier.
-directive("okay2", function() {
- return {
- bindToController: true,
- controllerAs: "$ctrl",
- controller: function() {
-
- },
- scope: {
- text: "@text"
- }
- };
-});
-```
-
-While the following are invalid:
-
-```js
-// BAD, because the controller property is a string with no identifier.
-directive("bad", function() {
- return {
- bindToController: true,
- controller: "noIdentCtrl",
- scope: {
- text: "@text"
- }
- };
-});
-
-
-// BAD because the controller is not a string (therefore has no identifier),
-// and there is no controllerAs property.
-directive("bad2", function() {
- return {
- bindToController: true,
- controller: function noControllerAs() {
-
- },
- scope: {
- text: "@text"
- }
- };
-});
-```
diff --git a/src/ng/compile.js b/src/ng/compile.js
index 02cd0bdbaa94..d769424c6366 100644
--- a/src/ng/compile.js
+++ b/src/ng/compile.js
@@ -360,9 +360,7 @@
*
* #### `bindToController`
* This property is used to bind scope properties directly to the controller. It can be either
- * `true` or an object hash with the same format as the `scope` property. Additionally, a controller
- * alias must be set, either by using `controllerAs: 'myAlias'` or by specifying the alias in the controller
- * definition: `controller: 'myCtrl as myAlias'`.
+ * `true` or an object hash with the same format as the `scope` property.
*
* When an isolate scope is used for a directive (see above), `bindToController: true` will
* allow a component to have its properties bound to the controller, rather than to scope.
@@ -1027,20 +1025,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
bindings.bindToController =
parseIsolateBindings(directive.bindToController, directiveName, true);
}
- if (isObject(bindings.bindToController)) {
- var controller = directive.controller;
- var controllerAs = directive.controllerAs;
- if (!controller) {
- // There is no controller, there may or may not be a controllerAs property
- throw $compileMinErr('noctrl',
- 'Cannot bind to controller without directive \'{0}\'s controller.',
- directiveName);
- } else if (!identifierForController(controller, controllerAs)) {
- // There is a controller, but no identifier or controllerAs property
- throw $compileMinErr('noident',
- 'Cannot bind to controller without identifier for directive \'{0}\'.',
- directiveName);
- }
+ if (bindings.bindToController && !directive.controller) {
+ // There is no controller
+ throw $compileMinErr('noctrl',
+ 'Cannot bind to controller without directive \'{0}\'s controller.',
+ directiveName);
}
return bindings;
}
@@ -2709,7 +2698,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var bindings = controllerDirective.$$bindings.bindToController;
if (preAssignBindingsEnabled) {
- if (controller.identifier && bindings) {
+ if (bindings) {
controller.bindingInfo =
initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
} else {
@@ -3412,8 +3401,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
- // Set up $watches for isolate scope and controller bindings. This process
- // only occurs for isolate scopes and new scopes with controllerAs.
+ // Set up $watches for isolate scope and controller bindings.
function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
var removeWatchCollection = [];
var initialChanges = {};
diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js
index e4a8ddf4e960..3126a2dad044 100755
--- a/test/ng/compileSpec.js
+++ b/test/ng/compileSpec.js
@@ -6126,154 +6126,113 @@ describe('$compile', function() {
});
- it('should throw noident when missing controllerAs directive property', function() {
- module(function($compileProvider) {
- $compileProvider.directive('noIdent', valueFn({
- templateUrl: 'test.html',
- scope: {
- 'data': '=dirData',
- 'oneway': '')($rootScope);
- }).toThrowMinErr('$compile', 'noident',
- 'Cannot bind to controller without identifier for directive \'noIdent\'.');
- });
- });
-
-
- it('should throw noident when missing controller identifier', function() {
- module(function($compileProvider, $controllerProvider) {
- $controllerProvider.register('myCtrl', function() {});
- $compileProvider.directive('noIdent', valueFn({
- templateUrl: 'test.html',
- scope: {
- 'data': '=dirData',
- 'oneway': '')($rootScope);
- }).toThrowMinErr('$compile', 'noident',
- 'Cannot bind to controller without identifier for directive \'noIdent\'.');
- });
- });
+ controllerAs: 'myCtrl'
+ }],
+ scopeOptions = [{
+ description: 'isolate scope',
+ scope: {}
+ }, {
+ description: 'new scope',
+ scope: true
+ }, {
+ description: 'no scope',
+ scope: false
+ }],
- it('should bind to controller via object notation (isolate scope)', function() {
- var controllerCalled = false;
- module(function($compileProvider, $controllerProvider) {
- $controllerProvider.register('myCtrl', function() {
- this.check = function() {
- expect(this.data).toEqualData({
- 'foo': 'bar',
- 'baz': 'biz'
- });
- expect(this.oneway).toEqualData({
- 'foo': 'bar',
- 'baz': 'biz'
- });
- expect(this.str).toBe('Hello, world!');
- expect(this.fn()).toBe('called!');
- };
- controllerCalled = true;
- if (preAssignBindingsEnabled) {
- this.check();
- } else {
- this.$onInit = this.check;
- }
- });
- $compileProvider.directive('fooDir', valueFn({
- templateUrl: 'test.html',
- bindToController: {
- 'data': '=dirData',
- 'oneway': 'isolate
');
- $rootScope.fn = valueFn('called!');
- $rootScope.whom = 'world';
- $rootScope.remoteData = {
- 'foo': 'bar',
- 'baz': 'biz'
- };
- element = $compile('')($rootScope);
- $rootScope.$digest();
- expect(controllerCalled).toBe(true);
- });
- });
+ templateOptions = [{
+ description: 'inline template',
+ template: 'template
'
+ }, {
+ description: 'templateUrl setting',
+ templateUrl: 'test.html'
+ }, {
+ description: 'no template'
+ }];
+ forEach(controllerOptions, function(controllerOption) {
+ forEach(scopeOptions, function(scopeOption) {
+ forEach(templateOptions, function(templateOption) {
+
+ var description = [],
+ ddo = {
+ bindToController: {
+ 'data': '=dirData',
+ 'oneway': 'template');
+ $rootScope.fn = valueFn('called!');
+ $rootScope.whom = 'world';
+ $rootScope.remoteData = {
+ 'foo': 'bar',
+ 'baz': 'biz'
+ };
+ element = $compile('')($rootScope);
+ $rootScope.$digest();
+ expect(controllerCalled).toBe(true);
+ if (ddo.controllerAs || ddo.controller.indexOf(' as ') !== -1) {
+ if (ddo.scope) {
+ expect($rootScope.myCtrl).toBeUndefined();
+ } else {
+ // The controller identifier was added to the containing scope.
+ expect($rootScope.myCtrl).not.toBeUndefined();
+ }
+ }
+ });
});
- expect(this.str).toBe('Hello, world!');
- expect(this.fn()).toBe('called!');
- };
- controllerCalled = true;
- if (preAssignBindingsEnabled) {
- this.check();
- } else {
- this.$onInit = this.check;
- }
+
+ });
});
- $compileProvider.directive('fooDir', valueFn({
- templateUrl: 'test.html',
- bindToController: {
- 'data': '=dirData',
- 'oneway': 'isolate');
- $rootScope.fn = valueFn('called!');
- $rootScope.whom = 'world';
- $rootScope.remoteData = {
- 'foo': 'bar',
- 'baz': 'biz'
- };
- element = $compile('')($rootScope);
- $rootScope.$digest();
- expect(controllerCalled).toBe(true);
});
+
});