I'm trying to write a generalized route in my application and resolve the view and controller names on the fly based on the route params.
I have the following code that works:
$stateProvider.state('default', angularAMD.route({
url: '/:module/:action?id',
templateUrl: function (params) {
var module = params.module;
var action = module + params.action.charAt(0).toUpperCase()
+ params.action.substr(1);
return 'app/views/' + module + '/' + action + 'View.html';
},
controller: 'userController',
}));
However, I'm unable to figure out a way to resolve the controller name dynamically. I tried using resolve
as described here, but ui-router seems to handle resolve differently than angular-route.
Any pointers?
EDIT: I've already tried using controllerProvider
but it doesn't work for me (for instance, the following code just returns a hard coded controller name to test whether it actually works):
controllerProvider: function () {
return 'userController';
}
Gives me the following error:
Error: [ng:areq] Argument 'userController' is not a function, got undefined
http://errors.angularjs.org/1.3.3/ng/areq?p0=userController&p1=not%20aNaNunction%2C%20got%20undefined
This is a link to working plunker.
We need two features of the UI-Router:
This would be our main.js, which contains smart conversion controllerName - controllerPath:
require.config({
//baseUrl: "js/scripts",
baseUrl: "",
// alias libraries paths
paths: {
"angular": "angular",
"ui-router": "angular-ui-router",
"angularAMD": "angularAMD",
"DefaultCtrl": "Controller_Default",
"OtherCtrl": "Controller_Other",
},
shim: {
"angularAMD": ["angular"],
"ui-router": ["angular"],
},
deps: ['app']
});
// Controller_Default.js
define(['app'], function (app) {
app.controller('DefaultCtrl', function ($scope) {
$scope.title = "from default";
});
});
// Controller_Other.js
define(['app'], function (app) {
app.controller('OtherCtrl', function ($scope) {
$scope.title = "from other";
});
});
Firstly we would need some method converting the param (e.g. id) into controller name. For our test purposes let's use this naive implementation:
var controllerNameByParams = function($stateParams)
{
// naive example of dynamic controller name mining
// from incoming state params
var controller = "OtherCtrl";
if ($stateParams.id === 1) {
controller = "DefaultCtrl";
}
return controller;
}
.state()
And that would be finally our state definition
$stateProvider
.state("default", angularAMD.route({
url: "/{id:int}",
templateProvider: function($stateParams)
{
if ($stateParams.id === 1)
{
return "<div>ONE - Hallo {{title}}</div>";
}
return "<div>TWO - Hallo {{title}}</div>";
},
resolve: {
loadController: ['$q', '$stateParams',
function ($q, $stateParams)
{
// get the controller name === here as a path to Controller_Name.js
// which is set in main.js path {}
var controllerName = controllerNameByParams($stateParams);
var deferred = $q.defer();
require([controllerName], function () { deferred.resolve(); });
return deferred.promise;
}]
},
controllerProvider: function ($stateParams)
{
// get the controller name === here as a dynamic controller Name
var controllerName = controllerNameByParams($stateParams);
return controllerName;
},
}));
Check it here, in this working example
As documented here: $stateProvider, for a state(name, stateConfig)
we can use controller
and controllerProvider
. Some extract from documentation:
...
controller
(optional) stringfunction
Controller fn that should be associated with newly related scope or the name of a registered controller if passed as a string. Optionally, the ControllerAs may be declared here.
controller: "MyRegisteredController"
controller:
"MyRegisteredController as fooCtrl"}
controller: function($scope, MyService) {
$scope.data = MyService.getData(); }
controllerProvider
(optional) function
Injectable provider function that returns the actual controller or string.
controllerProvider:
function(MyResolveData) {
if (MyResolveData.foo)
return "FooCtrl"
else if (MyResolveData.bar)
return "BarCtrl";
else return function($scope) {
$scope.baz = "Qux";
}
}
...
resolve
(optional) object
An optional
map<string, function>
of dependencies which should be injected into the controller. If any of these dependencies are promises, the router will wait for them ALL to be resolved before the controller is instantiated...
I.e. let's use controllerProvider
:
... to resolve the controller name dynamically...
In case, that you managed to get here, maybe you'd like to check another similar solution with RequireJS - angular-ui-router with requirejs, lazy loading of controller