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

Component controller #13742

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions docs/content/error/$componentController/multicomp.ngdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@ngdoc error
@name $componentController:multicomp
@fullName Multiple components defined
@description

This error occurs when multiple components or component-like directives
are defined with the same name, and processing them would result in a
collision or an unsupported configuration.


To resolve this issue remove one of the directives or controllers which is causing the collision.

Component-like directives are those that:

* Have a controller
* Have defined controllerAs
* Have restrict to 'E'
11 changes: 11 additions & 0 deletions docs/content/error/$componentController/nocomp.ngdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@ngdoc error
@name $componentController:nocomp
@fullName No component available
@description

This error occurs when it is requested a component that it is not defined.


To resolve this issue create the component which the requested name or change
the name to match an existing component name.

5 changes: 1 addition & 4 deletions src/ng/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -928,7 +928,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
return this;
};

this.$$componentControllers = createMap();
/**
* @ngdoc method
* @name $compileProvider#component
Expand Down Expand Up @@ -1054,8 +1053,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
*/
this.component = function registerComponent(name, options) {
var controller = options.controller || function() {};
var ident = identifierForController(options.controller) || options.controllerAs || '$ctrl';
this.$$componentControllers[name] = { controller: controller, ident: ident};

function factory($injector) {
function makeInjectable(fn) {
Expand All @@ -1071,7 +1068,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var template = (!options.template && !options.templateUrl ? '' : options.template);
return {
controller: controller,
controllerAs: ident,
controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl',
template: makeInjectable(template),
templateUrl: makeInjectable(options.templateUrl),
transclude: options.transclude,
Expand Down
22 changes: 19 additions & 3 deletions src/ngMock/angular-mocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -2182,11 +2182,27 @@ angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
* @return {Object} Instance of requested controller.
*/
angular.mock.$ComponentControllerProvider = ['$compileProvider', function($compileProvider) {
var minErr = angular.$$minErr('$componentController');
return {
$get: ['$controller', function($controller) {
$get: ['$controller','$injector', function($controller,$injector) {
return function $componentController(componentName, locals, bindings, ident) {
var controllerInfo = $compileProvider.$$componentControllers[componentName];
return $controller(controllerInfo.controller, locals, bindings, ident || controllerInfo.ident);
// get all directives associated to the component name
var directives = $injector.get(componentName + 'Directive');
// look for those directives that are components
var candidateDirectives = directives.filter(function(directiveInfo) {
// components have controller, controllerAs and restrict:'E'
return directiveInfo.controller && directiveInfo.controllerAs && directiveInfo.restrict === 'E';
});
// check if valid directives found
if (candidateDirectives.length === 0) {
throw minErr('nocomp','No \'{0}\' component found', componentName);
}
if (candidateDirectives.length > 1) {
throw minErr('multicomp','Found {0} directive candidates for component \'{1}\'', candidateDirectives.length, componentName);
}
// get the info of the component
var directiveInfo = candidateDirectives[0];
return $controller(directiveInfo.controller, locals, bindings, ident || directiveInfo.controllerAs);
};
}]
};
Expand Down
119 changes: 119 additions & 0 deletions test/ngMock/angular-mocksSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1938,6 +1938,125 @@ describe('ngMock', function() {
expect($scope.testCtrl).toBe(ctrl);
});
});

it('should instantiate the controller of the restrict:\'E\' component if there are more directives with the same name but not restricted to \'E\'', function() {
function TestController() {
this.r = 6779;
}
module(function($compileProvider) {
$compileProvider.directive('test', function() {
return { restrict: 'A' };
});
$compileProvider.component('test', {
controller: TestController
});
});
inject(function($componentController, $rootScope) {
var ctrl = $componentController('test', { $scope: {} });
expect(ctrl).toEqual({ r: 6779 });
});
});

it('should instantiate the controller of the restrict:\'E\' component if there are more directives with the same name and restricted to \'E\' but no controller', function() {
function TestController() {
this.r = 22926;
}
module(function($compileProvider) {
$compileProvider.directive('test', function() {
return { restrict: 'E' };
});
$compileProvider.component('test', {
controller: TestController
});
});
inject(function($componentController, $rootScope) {
var ctrl = $componentController('test', { $scope: {} });
expect(ctrl).toEqual({ r: 22926 });
});
});

it('should instantiate the controller of the directive with controller, controllerAs and restrict:\'E\' if there are more directives', function() {
function TestController() {
this.r = 18842;
}
module(function($compileProvider) {
$compileProvider.directive('test', function() {
return { };
});
$compileProvider.directive('test', function() {
return {
restrict: 'E',
controller: TestController,
controllerAs: '$ctrl'
};
});
});
inject(function($componentController, $rootScope) {
var ctrl = $componentController('test', { $scope: {} });
expect(ctrl).toEqual({ r: 18842 });
});
});

it('should fail if there is no directive with restrict:\'E\' and controller', function() {
function TestController() {
this.r = 31145;
}
module(function($compileProvider) {
$compileProvider.directive('test', function() {
return {
restrict: 'AC',
controller: TestController
};
});
$compileProvider.directive('test', function() {
return {
restrict: 'E',
controller: TestController
};
});
$compileProvider.directive('test', function() {
return {
restrict: 'EA',
controller: TestController,
controllerAs: '$ctrl'
};
});
$compileProvider.directive('test', function() {
return { restrict: 'E' };
});
});
inject(function($componentController, $rootScope) {
expect(function() {
$componentController('test', { $scope: {} });
}).toThrowMinErr('$componentController','nocomp', "No 'test' component found");
});
});

it('should fail if there more than two components with same name', function() {
function TestController($scope, a, b) {
this.$scope = $scope;
this.a = a;
this.b = b;
}
module(function($compileProvider) {
$compileProvider.directive('test', function() {
return {
restrict: 'E',
controller: TestController,
controllerAs: '$ctrl'
};
});
$compileProvider.component('test', {
controller: TestController
});
});
inject(function($componentController, $rootScope) {
expect(function() {
var $scope = {};
$componentController('test', { $scope: $scope, a: 'A', b: 'B' }, { x: 'X', y: 'Y' });
}).toThrowMinErr('$componentController','multicomp',"Found 2 directive candidates for component 'test'");
});
});
});
});

Expand Down