I have developed a Single Page App that uses a REST api. Users are required to login to access the application. When a user logs in they are redirected to /dashboard. On this URL / route, I would like to load a different template and controller based on the role of the user (e.g. normal user or admin user).
I have looked at https://github.com/angular-ui/ui-router/wiki under the templates section but none of the options support what I am trying to achieve.
Any solution should also load different controllers based on the user role, for example UserDashboardController or AdminDashboardController.
So essentialy what I need is a single route that loads a different template AND controller based on a user role variable that is set in a service when a user logs in.
Am I thinking along the right lines, or should I be implementing another solution?
Any help on this would be greatly appreciated.
While technically ui-router
templateUrl function does not support injecting services you can use templateProvider
to inject service
that holds role
variable or loads it asynchronously and then use $templateFactory
to return HTML content. Consider following example:
var app = angular.module('app', ['ui.router']);
app.service('session', function($timeout, $q){
this.role = null;
this.loadRole = function(){
//load role using axax request and return promise
};
});
app.config(function($stateProvider, $urlRouterProvider){
$stateProvider.state('dashboard', {
url: '/dashboard',
templateProvider: function(session, $stateParams, $templateFactory){
return session.loadRole().then(function(role){
if(session.role == 'admin'){
return $templateFactory.fromUrl('/admin/dashboard.html', $stateParams);
} else {
return $templateFactory.fromUrl('/user/dashboard.html', $stateParams);
}
});
}
});
$urlRouterProvider.otherwise('/dashboard');
});
As for controller
you can either state that you would like to use specific controller inside root element of each template with ng-controller
. Or similarly you can use controllerProvider
option to inject service
that will already have role
resolved by templateProvider
. Take a look at following example of controllerProvider
option inside ui-router
state definition:
controllerProvider: function(session){
if(session.role == 'admin'){
return 'AdminCtrl';
} else {
return 'UserCtrl';
}
}
Of course you can remove duplicates from this code easily and define a more accessible micro DSL to make defining different rules for particular roles and views easier.
The following demo should help you understand the code.
As usually this greatly depends on context. To help you come up with an answer let me suggest following questions first:
Are you going to hide only couple of button
s and other action elements basically making a page read only for regular users and editable for superusers? If the changes will be small I would probably go with using the same views and only hiding particular elements, probably forging a directive similar to ng-if
that would allow enabling/disabling particular functionality declaratively only-role='operator, admin'
. On the other hand if views are going to be vastly different then employing different templates can simplify markup greatly.
Do actions that look similar on surface differ in inner workings for different roles? In example if you have Edit action available both for user
and admin
role but in one case it starts wizard like UI and in other a complex form for advanced users then having a separate controller
makes more sense. On the other hand if admin
actions are a superset of user
actions then having single controller seems easier to follow. Note that in both cases keeping controller
things pays off - they should only glue views to behaviour that is encapsulated in services/view models/models/pick a name
For instance being able to provide navigation to particular page by simply writing ui-sref="dashboard"
regardless of the current user role
may be beneficial if it exists in various contexts. If that's the case then having them defined under single route/state seems more maintainable then a conditional logic used to build different ui-sref
/ng-href
based on role. However you could also define routes/states dynamically based on user role - loaded dynamically or not
Sometimes we first build features for regular users then for premium and then for ultimate. It's not unusual to divide work on pages for user
and admin
between team members especially if clear boundaries can be drawn easily. In such case having separate views
and controllers
can simply developers work avoiding conflicts. Of course it's not all rainbows and unicorns - team must be very disciplined to remove duplication that most likely will happen.
Hope that my suggestions will help you decide.
Am I thinking along the right lines, or should I be implementing another solution?
IMO, You should not do it this way.
Here, I propose 2 other solutions depending on how your application is implemented.
1) If the rights of your roles can be configured (you could have a separate page to configure your roles, assign rights to your roles,...). Then use only 1 template and 1 controller for your roles (normal users, admin users, and more......) and use ng-show
, ng-class
,.. to display your HTML accordingly.
In this case, we don't care much whether the user is normal user or admin user, that's just the name of our role. What we do care about is the rights and it's dynamic => Therefore, we should display the html dynamically based on the configured rights (for sure, there are also checks on server side when users perform an action to prevent the user from crafting a malicious http request and posting to server). If we were to use separate templates for that, there are countless cases.
The point of this solution is that the functions of the page are the same to your roles, you just need to show/hide the functions of the page based on the user.
2) If the rights of the roles are fixed (cannot be configured) and the functionality of the views for normal users and admin users are different. It's better to use separate states for these views and authorize access to these views based on the logged-in user (for sure, there is also authorization on server side when users perform an action).
The reason is: the admin user view and normal user view have different functionality (which should be separated from each other)
If you are using the a version of angular greater than 1.2 you can do a directive with a templateUrl as a function.
So the basic ideas is you have a dashboard view that has a custom directive on it that will determine the template based on the user level. So something like this:
(function () {
'use strict';
angular.module('App.Directives')
.directive('appDashboard', ['UserManager', function (UserManager) {
return {
restrict: 'EA',
templateUrl: function(ele, attr){
if (UserManager.currentUser.isAdmin){
return 'admin.html';
}else{
return 'user.html';
}
}
};
}]);
})();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With