Custom Root URIs For User Profiles with AngularJS

internrocket.com
3 min readJan 23, 2015

A quick and noninvasive strategy using redirects.

Introduction

Hello from the internrocket.com development team! We’re a small startup based in Bloomington, IL with the goal of helping people find their perfect career. This is the first of many tutorials from the unique development strategies we used to create the site. In the future we’ll be discussing topics such as security, JavaScript, Java EE, CSS3, and more.

We made the choice to build in Angular from the beginning, back when it was at version 1.0.1 in 2012. There have been many lessons learned by our team in the years since, and we think it’s about time we share that knowledge back to the Angular community.

If you have any questions or suggestions tweet us @internrocket!

Now on with the tutorial!

What is a Custom Root URI?

What we mean is something similar to the URI you have for your Twitter or Facebook profile e.g https://twitter.com/internrocket, https://facebook.com/internrocket. Everything after the domain name is just your profile name, so we say this is at the “root” of the path.

Requirements

We wanted a system robust enough that wouldn’t require us to have to plan out every root URI we would ever need while still supporting as many custom URIs as possible. Twitter implements a system where some of their non-custom root URIs fall under a “/i/” e.g. twitter.com/i/notifications. While most links could follow the twitter “/i/” pattern, it was important that the system supported at least some root level URIs especially the ones visible to visitors that are not yet signed in such as internrocket.com/404. Additionally we started integrating our system after we had developed a significant portion of the website, so we wanted to have as little effect on the existing code base as possible. We didn’t want to have to rewrite or refactor every existing link on the site.

Before the URI change

Our routes would have looked something like this in our module config using $routeProvider directly:

$routeProvider.when('/', {
templateUrl: '/views/home.html',
controller: 'homeController'
}).when('/404', {
templateUrl: '/views/404.html',
controller: '404Controller'
}).when('/login', {
templateUrl: '/views/login.html',
controller: 'loginController'
}).when('/explore', {
templateUrl: '/views/explore.html',
controller: 'exploreController'
}).when('/intern/:id', {
templateUrl: '/views/profile-user.html',
controller: 'profileUserController'
}).otherwise({
redirectTo: '/404'
});

In that example we have 3 URIs that need to stay at the root: “/”, “/404", and “/login”. The others, “/explore” and “/intern/:id/”, can be put under a “/i/” prefix.

Our links throughout the site were like this:

<a ng-href="/explore">Explore</a>
<a ng-href="/login">Login</a>

Location changes in controllers were like this:

$location.path("/explore");

Since we had so many links and references to $location.path() we decided to avoid the possibility of having to write in “/i/” to these links.

Hijacking $routeProvider

The solution to all our requirements was developing a system of registering the entirety of our route config as a set of redirects before registering all of our routes under an “/i/” prefix.

In the module config e.g. angular.module(‘myApp’, []).config(…) before route config:

var getPath = function(path) {
return '/i' + (path.indexOf('/') === 0 ? path : '/' + path);
};
var config = {
when: function(path, route) {
if(route.overrideRoot) {
$routeProvider.when(path, route);
} else {
var redirect = angular.copy(route);
delete redirect.templateUrl;
delete redirect.controller;
redirect.redirectTo = getPath(path);
$routeProvider
.when(path, redirect)
.when(getPath(path), route);
}
return this;
}, otherwise: function(config) {
$routeProvider.otherwise(config);
return this;
}
};

This new config object will replace $routeProvider when you register your routes.

The new routes look like this:

config.when('/', {
templateUrl: '/views/home.html',
controller: 'homeController',
overrideRoot: true
}).when('/404', {
templateUrl: '/views/404.html',
controller: '404Controller',
overrideRoot: true
}).when('/login', {
templateUrl: '/views/login.html',
controller: 'loginController',
overrideRoot: true
}).when('/explore', {
templateUrl: '/views/explore.html',
controller: 'exploreController'
}).when('/intern/:id', {
templateUrl: '/views/profile-user.html',
controller: 'profileUserController'
}).when('/:id', {
templateUrl: '/views/custom-url.html',
controller: 'customUrlController',
overrideRoot: true
}).otherwise({
redirectTo: '/404'
});

Note the addition of .when(‘/:id’, …). That’s where the new custom URI route is!

Results

Now we can safely navigate to “/”, “/404", and “/login” as before; however, when we navigate to “/explore”, or “/intern/:id” we get redirected to “/i/explore” and “/i/intern/:id”. None of our existing links need to be changed! We now have the ability to navigate to user profiles at the root of the path e.g. https://internrocket.com/internrocket!

Cheers!

We’ll be back next time with some more interesting things you can do with $routeProvider by tapping into your route config from within your controllers and services including some sweet authenticated routes!

--

--