Saturday, March 4, 2017

AngularJS Dynamic Routing


Hi,

Here I will explain how to use single Route to load partial views dynamically based on Menu links.

The implementation idea for dynamic routing came from:
  1. http://stackoverflow.com/questions/13681116/angularjs-dynamic-routing 
  2. My previous Blog post: https://rajudasa.blogspot.in/2016/04/dynamically-loading-angularjs.html 
  3. and some research. 
Scenario:
We know that client side code is accessible to end users.
Our application is a SPA, created using AngularJS and it has roles based authorization.
You don't want to show all AngularJS Route mappings defined in your application to all role users, since a low role user/hacker (customer) may try to access views defined for high role users (Admin).
Eventhough we may have setup authorization security for operations at server side, hiding Route mappings at client side provides a first level of security.

Approach:
Our application renders specific Menu links based on authenticated user role, it has only single route mapping (wildcard) to handle all partial view links with dynamic controller. This controller handles registering new route, loading template and controller dynamically based on Menu link clicked.
Since controllers are also loaded dynamically, other role users can't access the controller logic intended for particular role.
To make this approach work, our Menu links point to the single wildcard route which is then converted to actual route by the dynamic controller.

Menu Links:
<div ng-controller="home">
 <h3>Home page</h3>
 <a href="" ng-click="go('/app/view-one')">second view1</a>
 <a href="" ng-click="go('/app/view2')">second view2</a>
 <a href="Default.html#/app/view3">second view3</a>
 <hr />
 <div ng-view></div>
</div>


Single Rout Map:
var app = angular.module("app", ['ngRoute']);
app.config(function ($routeProvider, $controllerProvider) {
 $routeProvider
 .when('/app/:name*', { template: '<div ng-init="load()">Loading...</div>', 
             controller: DynamicController })
 .otherwise({
  redirectTo: '/'
 });
 
 app.register =
 {
  controller: $controllerProvider.register,
  router: $routeProvider,
  loader: new RouteProvider()
 };
});


Dynamic Controller:
function DynamicController($scope, $routeParams, $route, $location) {
 var name = $routeParams.name;
 if ($route.routes['/' + name] == null)
  app.register.router.when('/' + name, app.register.loader.resolve(name));
 $scope.load = function () {
  $location.path('/' + name);
 };
}

From above code, you can see all link paths start with "/app/viewName" which are then converted to "/viewName".
Note that viewName is also used as file name for template and controller files, if you want you can modify the code to follow your naming convention for those files.


DisAdvantage:
$routeProvider is leaked into global scope.

Download Link:
For full code, download from here.