Pretty Url in AngularJS and Loopback. Drop the '#'
Georges Biaux3 min read
AngularJS routing system is great to create RESTful single-page applications, but it comes at the cost of accepting the # fragment in all your urls. There are several reasons you would like to drop this tiny character:
- Search Engine Optimization (SEO) considerations
- Use Anchors in your pages and urls
Good news, you can easily configure your application to go from:
myapp.com/#/my/angular/routes
To:
myapp.com/my/angular/routes
There are two things that need to be done:
- Configuring AngularJS to enable HTML5 mode
- Configuring your backend framework to redirect all non-REST and non-static-asset HTTP requests to the frontend index.html
Configuring your backend is necessary to tell your server to redirect the “/my/angular/routes” to the angular app, and avoid getting 404 errors. Here I will be using Loopback as an example of REST API framework, but it can be easily adapted to other frameworks like Spring or Symfony.
Step 1: Configuring AngularJS to enable HTML5 mode
This step will depend on which version of Angular you’re using.
Angular 1 with angular-route or angular-ui-router
When you set your angular application configuration, you simply have to use the $locationProvider module and set html5Mode to true.
angular.module('app')
.config(config)
config.$inject = ['$locationProvider'];
function config($locationProvider) {
$locationProvider.html5Mode(true);
}
To tell Angular what is the base path of your application, provide a base tag to your index.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<base href="/">
</head>
Angular 2
The equivalent of HTML5 mode in Angular 2 is to use the PathLocationStrategy
as the router strategy.
import {ROUTER_PROVIDERS, APP_BASE_HREF} from 'angular2/router';
bootstrap(yourApp, [
ROUTER_PROVIDERS, // includes binding to PathLocationStrategy
provide(APP_BASE_HREF, {useValue: '/'})
]);
You can edit the APP_BASE_HREF
to define your application base path
provide(APP_BASE_HREF, {useValue: '/my/app/path'})
Step 2: Configuring your backend
The above configuration will work on its own until you try to access an angular route directly with its url, because your web server won’t know he has to redirect this url to your angular application.
To fix that, you must configure your backend framework to redirect all your angular route urls to the index.html.
To do this, add a filter to the server.js file.
var path = require('path');
//List here the paths you do not want to be redirected to the angular application (scripts, stylesheets, templates, loopback REST API, ...)
var ignoredPaths = ['/vendor', '/css', '/js', '/views', '/api'];
app.all('/*', function(req, res, next) {
//Redirecting to index only the requests that do not start with ignored paths
if(!startsWith(req.url, ignoredPaths))
res.sendFile('index.html', { root: path.resolve(__dirname, '..', 'client') });
else
next();
});
function startsWith(string, array) {
for(i = 0; i < array.length; i++)
if(string.startsWith(array[i]))
return true;
return false;
}
Now, all your requests that do not match the specified patterns will be taken care of by the angular routing system and not the Loopback one.
The only disadvantage is that you have to be careful when adding new assets or REST endpoints and be sure that their urls do not conflict with angular routes.
Conclusion
Congratulations! You have a fully functional angular application without any trace of # in urls!
What’s next? You could dive deeper into Angular state management features and implement basic route authorization in AngularJS.