The role of roles
Roles are quite an important concept in most web applications. Not all users will have access to everything and it should be easy from a development point of view to restrict access to parts of the app from certain users.
In an AngularJS application you have a choice of a couple of popular routing modules, none of which contain support for role-based routing. We wanted a solution that builds on top of the ngRoute module to allow us to specify different routes for different users.
The AngularJS team maintains an "official" routing solution in the
ngRoute module. If you've ever built an
Angular app you'll probably be familiar with its configuration:
In the above example we define a couple of routes. When the path of the URL in the browser matches one of the strings passed to the route provider the relevant template will be displayed. This means we can use normal links throughout our templates and Angular will take care of the rest:
The above template shows two different ways of handling routes within markup. The
ngHref directive allows you to use
Angular's interpolation syntax to dynamically insert a value into the URL and the normal
href attribute allows you use
a simple string.
There are three main deficiencies with this approach. Firstly, you have to know the exact URL for every view of your application. Secondly, if a URL was to change you'll have to search through your entire codebase and replace every occurrence of the old string with the new one. Finally, if the current user is not able to access a particular view then there will have to be logic somewhere to handle this.
Typically there are three common ways of doing so, all of which exhibit some problems:
You can place some logic at the top of the controller of the restricted route. This is not a great solution because the route change is allowed to go ahead and the user will experience a flash of the empty new template before quickly being redirected elsewhere.
You can use a location change event handler and prevent the default action if it should not be allowed. This approach is poorly documented and difficult to get working. Of particular confusion is the inability to use a route change event handler.
You can define different routes for each type of user and use the
ngShowdirectives to display different links appropriately. This leads to bloated markup and the potential for users to briefly see links that they shouldn't if you don't properly cloak them until your controller is ready.
Introducing the mmRoute module
We built the
mmRoute module with those three concerns in mind. Instead of attempting to be a complete
routing solution it builds on top of
ngRoute which means you already know most of what you need to configure it and it
benefits from the stability of code that is used in production across the web.
The service is configured via its provider in your app config just as the
ngRoute service is. However, instead of
focusing on URLs the configuration focuses on roles. Roles are configured via the provider too and would generally
an Angular module
config function and output it onto the page.
Notice how in the above example the
mmRoute service is exposed on the root scope. Because Angular scopes
prototypically inherit from the root scope this means we are able to access
routes in any descendant
scope. We can rewrite the "account" view from above as follows:
This time we don't have any hard-coded URLs. Instead we ask the
mmRoute service to get the relevant URL for each
feature based on the roles available to the active user.
You can install the module through Bower with
bower install ng-mm-route and it's also available on GitHub.
We're always happy to hear feedback and accept improvements to our projects. Don't hesitate to fork it and send us back
pull requests if you have any modifications.