I needed a lightweight routing component in one of my AngularJS projects. The existing solutions like UI router and the Angular Router are way to heavy for my requirements. I only had the following requirements:
- One controller/view loaded for #pageA
- Another controller/view loaded for #pageB
I decided to write my own router component, to be sure the routing component has an absolutely minimum footprint (around 2 kb, unminified).
The basic HTML structure:
<div ng-controller="RoutingController as router">
<div ng-if="router.route === 'pageA'" ng-controller="PageAController"></div>
<div ng-if="router.route === 'pageB'" ng-controller="PageBController"></div>
</div>
The RoutingController uses a Router factory and a RouterEventsService, and is defined like this:
function RoutingController($scope, Router, RoutingEventsService) {
var controller = this;
controller.route = 'pageA';
var router = new Router;
router.addRoute('pageA');
router.addRoute('pageB');
RoutingEventsService.listen(locationHashChanged);
$scope.$on('$destroy', function() {
RoutingEventsService.stop(locationHashChanged);
});
function locationHashChanged() {
var currentRoute = router.route(window.location.hash);
if (!currentRoute) {
return false;
}
// scroll to top after switching route
$('html,body').animate({scrollTop: 0}, 50);
controller.route = currentRoute.name;
$scope.$apply(); // RoutingEvents are defined outside the angular scope
}
}
The Router factory function is defined like this:
function Router() {
var m = this;
m.routes = [];
m.default = null;
m.addRoute = function(name) {
var route = {name: name};
m.routes.push(route);
if (!m.default) {
m.default = route;
}
};
m.getDefault = function() {
return m.default;
};
m.route = function(hash) {
var matched;
angular.forEach(m.routes, function(route) {
if (hash === '#/' + route.name) {
matched = route;
}
});
if (!matched) {
matched = m.getDefault();
}
return matched;
};
}
The RouterEventsService is defined like this:
function RoutingEventsService($window) {
var service = this;
service.listen = function(fn) {
if ($window.addEventListener) {
$window.addEventListener('load', fn);
$window.addEventListener('hashchange', fn);
} else if($window.attachEvent) {
$window.attachEvent('load', fn);
$window.attachEvent('hashchange', fn);
}
};
service.stop = function(fn) {
if ($window.removeEventListener) {
$window.removeEventListener('load', fn);
$window.removeEventListener('hashchange', fn);
} else if($window.detachEvent) {
$window.detachEvent('load', fn);
$window.detachEvent('hashchange', fn);
}
};
}