feat($controller): support controller registration via $controllerProvider

It's now possible to register controllers as:

.register('MyCtrl', function($scope) { ... });
// or
.register('MyCtrl', ['$scope', function($scope) { ... });

Additionally a module loader shortcut api was added as well:

myModule.controller('MyCtr', function($scope) { ... });
This commit is contained in:
Igor Minar 2012-03-26 13:01:24 -07:00
parent 4b8d926062
commit d54dfecb00
5 changed files with 114 additions and 12 deletions

View file

@ -167,13 +167,24 @@ function setupModuleLoader(window) {
* @ngdoc method
* @name angular.Module#filter
* @methodOf angular.Module
* @param {string} name filter name
* @param {string} name Filter name.
* @param {Function} filterFactory Factory function for creating new instance of filter.
* @description
* See {@link angular.module.ng.$filterProvider#register $filterProvider.register()}.
*/
filter: invokeLater('$filterProvider', 'register'),
/**
* @ngdoc method
* @name angular.Module#controller
* @methodOf angular.Module
* @param {string} name Controller name.
* @param {Function} constructor Controller constructor function.
* @description
* See {@link angular.module.ng.$controllerProvider#register $controllerProvider.register()}.
*/
controller: invokeLater('$controllerProvider', 'register'),
/**
* @ngdoc method
* @name angular.Module#directive

View file

@ -1,6 +1,32 @@
'use strict';
/**
* @ngdoc object
* @name angular.module.ng.$controllerProvider
* @description
* The {@link angular.module.ng.$controller $controller service} is used by Angular to create new
* controllers.
*
* This provider allows controller registration via the
* {@link angular.module.ng.$controllerProvider#register register} method.
*/
function $ControllerProvider() {
var controllers = {};
/**
* @ngdoc function
* @name angular.module.ng.$controllerProvider#register
* @methodOf angular.module.ng.$controllerProvider
* @param {string} name Controller name
* @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
* annotations in the array notation).
*/
this.register = function(name, constructor) {
controllers[name] = constructor;
};
this.$get = ['$injector', '$window', function($injector, $window) {
/**
@ -8,8 +34,14 @@ function $ControllerProvider() {
* @name angular.module.ng.$controller
* @requires $injector
*
* @param {Function|string} Class Constructor function of a controller to instantiate, or
* expression to read from current scope or window.
* @param {Function|string} constructor If called with a function then it's considered to be the
* controller constructor function. Otherwise it's considered to be a string which is used
* to retrieve the controller constructor using the following steps:
*
* * check if a controller with given name is registered via `$controllerProvider`
* * check if evaluating the string on the current scope returns a constructor
* * check `window[constructor]` on the global `window` object
*
* @param {Object} locals Injection locals for Controller.
* @return {Object} Instance of given controller.
*
@ -20,14 +52,17 @@ function $ControllerProvider() {
* a service, so that one can override this service with {@link https://gist.github.com/1649788
* BC version}.
*/
return function(Class, locals) {
if(isString(Class)) {
var expression = Class;
Class = getter(locals.$scope, expression, true) || getter($window, expression, true);
assertArgFn(Class, expression);
return function(constructor, locals) {
if(isString(constructor)) {
var name = constructor;
constructor = controllers.hasOwnProperty(name)
? controllers[name]
: getter(locals.$scope, name, true) || getter($window, name, true);
assertArgFn(constructor, name, true);
}
return $injector.instantiate(Class, locals);
return $injector.instantiate(constructor, locals);
};
}];
}

View file

@ -54,6 +54,25 @@ describe('ng-view', function() {
});
it('should support string controller declaration', function() {
var MyCtrl = jasmine.createSpy('MyCtrl');
module(function($controllerProvider, $routeProvider) {
$controllerProvider.register('MyCtrl', ['$scope', MyCtrl]);
$routeProvider.when('/foo', {controller: 'MyCtrl', template: '/tpl.html'});
});
inject(function($route, $location, $rootScope, $templateCache) {
$templateCache.put('/tpl.html', [200, '<div></div>', {}]);
$location.path('/foo');
$rootScope.$digest();
expect($route.current.controller).toBe('MyCtrl');
expect(MyCtrl).toHaveBeenCalledWith(element.contents().scope());
});
});
it('should load content via xhr when route changes', function() {
module(function($routeProvider) {
$routeProvider.when('/foo', {template: 'myUrl1'});

View file

@ -38,6 +38,7 @@ describe('module loader', function() {
value('k', 'v').
filter('f', 'ff').
directive('d', 'dd').
controller('ctrl', 'ccc').
config('init2').
constant('abc', 123).
run('runBlock')).toBe(myModule);
@ -52,6 +53,7 @@ describe('module loader', function() {
['$provide', 'value', ['k', 'v'] ],
['$filterProvider', 'register', ['f', 'ff'] ],
['$compileProvider', 'directive', ['d', 'dd'] ],
['$controllerProvider', 'register', ['ctrl', 'ccc']],
['$injector', 'invoke', ['init2'] ]
]);
expect(myModule._runBlocks).toEqual(['runBlock']);

View file

@ -1,12 +1,47 @@
'use strict';
describe('$controller', function() {
var $controller;
var $controllerProvider, $controller;
beforeEach(inject(function($injector) {
$controller = $injector.get('$controller');
beforeEach(module(function(_$controllerProvider_) {
$controllerProvider = _$controllerProvider_;
}));
beforeEach(inject(function(_$controller_) {
$controller = _$controller_;
}));
describe('provider', function() {
it('should allow registration of controllers', function() {
var FooCtrl = function($scope) { $scope.foo = 'bar' },
scope = {},
ctrl;
$controllerProvider.register('FooCtrl', FooCtrl);
ctrl = $controller('FooCtrl', {$scope: scope});
expect(scope.foo).toBe('bar');
expect(ctrl instanceof FooCtrl).toBe(true);
});
it('should allow registration of controllers annotated with arrays', function() {
var FooCtrl = function($scope) { $scope.foo = 'bar' },
scope = {},
ctrl;
$controllerProvider.register('FooCtrl', ['$scope', FooCtrl]);
ctrl = $controller('FooCtrl', {$scope: scope});
expect(scope.foo).toBe('bar');
expect(ctrl instanceof FooCtrl).toBe(true);
});
});
it('should return instance of given controller class', function() {
var MyClass = function() {},
ctrl = $controller(MyClass);