Skip to content

Instantly share code, notes, and snippets.

@niallsmart
Last active December 20, 2015 17:19
Show Gist options
  • Save niallsmart/6168154 to your computer and use it in GitHub Desktop.
Save niallsmart/6168154 to your computer and use it in GitHub Desktop.
ngProviderController implements controller resolution using $injector.
The MIT License
Copyright (c) 2013 Niall Smart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
/*jslint vars: true */
/*global angular: true */
/**
* ngProviderController implements controller resolution using $injector. It attempts to
* resolve named controllers using $injector.get, otherwise falling back to the default
* resolution strategy.
*/
angular.module('ngProviderController', ['ng'], ['$provide', function($provide) {
"use strict";
var CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/,
decorator = function($injector, $delegate) {
return function(expression, locals) {
var instance, match, constructor, identifier;
if (angular.isString(expression)) {
match = expression.match(CNTRL_REG);
constructor = match[1];
identifier = match[3];
if ($injector.has(constructor)) {
expression = $injector.get(constructor);
if (!angular.isFunction(expression)) {
throw new Error('Argument \'' + constructor + '\' is not a function');
}
}
}
if (angular.isFunction(expression)) {
instance = $injector.instantiate(expression, locals);
if (identifier) {
if (!locals || typeof locals.$scope !== 'object') {
throw new Error('Can not export controller as "' + identifier + '". ' +
'No scope object provided!');
}
locals.$scope[identifier] = instance;
}
} else {
instance = $delegate.call(null, expression, locals);
}
return instance;
};
};
$provide.decorator('$controller', ['$injector', '$delegate', decorator]);
}]);
/*jslint vars: true, white: true */
/*global beforeEach: true, module: true, describe: true, it: true, expect: true */
describe('ngProviderController', function() {
"use strict";
var $provide;
var $controller;
var $controllerProvider;
beforeEach(module(function(_$provide_, _$controllerProvider_) {
$provide = _$provide_;
$controllerProvider = _$controllerProvider_;
}));
beforeEach(module("ngProviderController"));
beforeEach(inject(function(_$controller_) {
$controller = _$controller_;
}));
it('should return instance of given controller class', function() {
var MyCtrl = function($scope) {},
ctrl = $controller(MyCtrl, {$scope: {}});
expect(ctrl).toBeDefined();
expect(ctrl instanceof MyCtrl).toBe(true);
});
it('should return instance from provider', function() {
var MyCtrl,
scope = {},
ctrl;
$provide.factory("Ctrl", function() {
return MyCtrl = function($scope, name) {
$scope.name = name;
};
});
ctrl = $controller("Ctrl", {$scope: scope, name: "dave"});
expect(ctrl).toBeDefined();
expect(ctrl instanceof MyCtrl).toBe(true);
expect(scope.name).toBe("dave");
});
it('should publish controller instance into scope', function() {
var MyCtrl = function() {},
scope = {},
ctrl;
$provide.factory("Ctrl", function() {
return MyCtrl;
});
ctrl = $controller("Ctrl as ctrl", {$scope: scope});
expect(ctrl).toBeDefined();
expect(ctrl instanceof MyCtrl).toBe(true);
expect(scope.ctrl).toBe(ctrl);
});
it('should throw an error if $scope is not provided', function() {
$provide.value('Ctrl', function() {});
expect(function() {
$controller('Ctrl as ctrl');
}).toThrow('Can not export controller as "ctrl". No scope object provided!');
});
it('should fallback to default resolution strategy', function() {
var MyCtrl = function($scope, name) { $scope.name = name; },
scope = {},
ctrl;
$controllerProvider.register("Ctrl", MyCtrl);
ctrl = $controller("Ctrl", {$scope: scope, name: "dave"});
expect(ctrl).toBeDefined();
expect(ctrl instanceof MyCtrl).toBe(true);
expect(scope.name).toBe("dave");
});
it('should throw exception if provider doesn\'t return function', function() {
$provide.factory("Ctrl", function() {
return "ctrl";
});
expect(function() { $controller("Ctrl as c"); }).toThrow("Argument 'Ctrl' is not a function");
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment