Skip to content

AngularJS routing tips and tricks

2014 February 25
by Alec

Routing is a necessary component to any single-page web application. In a traditional web application, links are requested from the server, which renders complete new pages. In a typical Angular app, though, the browser requests both HTML fragments and some data to operate on and display in the HTML. URLs are prefixed with a hash (#), or in “HTML5 mode,” can just be appended to the root URL (with a little server configuration).

One of AngularJS’s primary criticisms is its routing framework. It is certainly lacking, mostly because you can only have one ng-view tag defined — every route’s template will be inserted here. Have another section of HTML that you’d like to change based on the current link? Too bad, you’ll have to hack your own solution using e.g. ng-show.

Once I understood the inherent limitations, though, it became clear that placing additional app-specific configuration under each route definition could be quite powerful. For instance:

$routeProvider.when('/request/document', {
  templateUrl: templatesDir + '/request/document.html', 
  controller: 'RequestDocumentController',
  kts: {
    title: "Request New Document",
    role: "Requestor"
  }
});

The first two configurations for the URL /request/document (templateUrl and controller) are common, and required by $routeProvider. However, the next two are not. I provide a hardcoded title and role, too. For templates with static titles, this works great, and seems like a logical place to define this information. This title can be used both in the <title> tag, and in another section of HTML outside the ng-view definition. The role definition represents a role that is required in order for the user to be able to access the ‘page.’ Here is a snippet of code showing them in use:

.controller('AppController', function($scope, $rootScope, $filter, $location) {
  $rootScope.$on("$routeChangeSuccess", function(event, current, previous) {
    var ktsConfig = current.kts;
    if(ktsConfig) {
      $scope.setTitle(ktsConfig.title || "");
      if(ktsConfig.role && !$filter('hasRole')($scope.user, ktsConfig.role)) {
        $location.path('/unauthorized');
      }
    }
  });
 
  $scope.setTitle = function(title) {
    $rootScope.title = title;
  };
});

AppController in this case is a parent controller to that controlled by the router. In other words, the ng-view tag is inside a div with ng-controller="AppController". After each “page load” (meaning a triggered route), Angular fires a $routeChangeSuccess event. If there is a title defined, it is set on the $rootScope[1] such that html tags outside of the ng-view declaration can display it. Additionally, we do a role check, redirecting the user to an ‘unauthorized’ page if they don’t possess the required role(s) [2].

Clearly, this technique could be extended to define much more route-specific data. I have used it to customize a generic loading dialog per-route, show or hide a map, etc. Use your imagination. Any similar data you keep repeating on most (or all) templates might be a good candidate.

Footnotes:
[1] In general, it is poor practice to share scope variables between controllers, as it breaks encapsulation. However, I don’t know of any other way to use interpolation inside the <title> tag.
[2] Your app may require more stringent security &emdash; for instance, in this case, the user could still hit the template URL directly through a browser and load the non-interpolated html. In my apps, I figure that in this case we’re likely only showing hard-coded templates without any sensitive application data.

2 Responses
  1. Darshan JOSHI permalink
    June 2, 2014

    Hi,

    For setting up the title of the page, we can utilize a “Page” service. This service can have a setTitle() function/API which can be consumed by all. if required during routeConfiguration, we need to write a PageServiceProvider.

    Thanks for roles filter and idea..

    • Alec permalink*
      June 2, 2014

      Hello,

      Yes, a “Page” service is not a bad idea. Providing it in the application’s config stage does add another layer of complexity, though. I deliberately chose implementation simplicity over optimal modularization/reuse — pick your tradeoffs wisely!

Comments are closed.