AngularJS : Change title based on Route
AngularJS is used to build single page WebApps. Considering it's nature, you will only have 1 Title for your page, so even if you go to '/product' or '/about', you would see the same Title on the Browser. Not really user friendly...
Where is one possible solution around this problem.
On your HTML, change the title tag to include a ng-bind:
<title ng-bind="'MyApp - ' + $root.title">MyApp - Welcome</title>
On your app.js:
$routeProvider
.when('/product', {templateUrl: '/partials/product.html', controller: 'ProductCtrl', title: 'Discover our Product'})
.when('/about', {templateUrl: '/partials/about.html', controller: 'AboutCtrl', title: 'About US'});
on your run(), add a $rootScope.$on:
$rootScope.$on("$routeChangeSuccess", function(currentRoute, previousRoute){
//Change page title, based on Route information
$rootScope.title = $route.current.title;
});
Note: The reason I'm using ng-bind instead of the angular template bind '{{ expression }}' is expressed on the documentation :
It is preferrable to use ngBind instead of {{ expression }} when a template is momentarily displayed by the browser in its raw state before Angular compiles it.
Written by Marco da Silva
Related protips
13 Responses
Great article! Very useful :)
Yes, Great Article! I have been looking for a nice way to do this. Thanks!
There is also ng-bind-template that will let you do the binding more cleanly.
Nice trick!
Minor enhancement:
$rootScope.$on("$routeChangeSuccess", function(event, currentRoute, previousRoute) {
$rootScope.title = currentRoute.title;
});
This way you do not have to inject $route in run().
Note that the $routeChangeSuccess event takes 3 parameters.
Thank you for this tip. I just don't know why there isn't any documentation about the $root used in the view. It just seems to not even exists on Angularjs.org
Every variable you reference from a binding (be it ng-bind
or {{}}
) is treated as a scope property. That means {{window.location.href()}}
won't work unless you also do $scope.window = $window
(and inject $window
, of course). It also means that the $root
in this view is actually referring to $scope.$root
. This is a property of the $rootScope
that refers to $rootScope
itself, and since every non-isolate scope inherits from its parents, $scope.$root
- or $root
in a binding - gets you $rootScope
.
You're right, though. This should be documented. The Angular docs are notoriously incomplete.
You don't have any dates anywhere on this page, not even in comments! So I'm not sure how long ago this was written. But if you're using UI-Router, it's pretty easy. Here's a sample indx.html that shows it in action. https://github.com/angular-ui/ui-router/blob/master/sample/index.html
Note the
<title ng-bind="$state.current.name">My App name - this text will be replaced</title>
@tylercollier it was already written some months ago. Thanks for the tip on ui-router ;)
This is good! Many other solutions that I saw involved changing the title manually in controllers, which is acceptable if you have 3 or 5 controllers, not up to 20 or more... Making the title an attribute of the route was certainly the most elegant way to go. I'm just concerned about having to bind the title attribute manually to the $rootScope
on $routeChangeSuccess
. I'll try to find a yet cleaner way and post here :) Um abraço!
Hey guys, is there any way we can have configurable page titles? I have created a service with a getter/setter method, and I call it on $stateChangeSuccess, to set the page title in the $scope, where I can pull it from later on on the view. Is this the right way to do it?
This looks really great. Do you know if there is any way to make the title, dynamic. Set it to values from the routeParam perhaps?
You can use $interpolate[1] like so:
$rootScope.$on("$routeChangeSuccess", function(currentRoute, previousRoute){
//Change page title, based on Route information
var titleExp = $interpolate($route.current.title);
$rootScope.title = titleExp($route.current.params);
});
Thanks you very much for the idea @ibrahima !
With ui-router you can set the window title like this (kinda hacky!):
$rootScope.$on('$stateChangeSuccess', function() {
var titleExp = $interpolate($state.current.data.pageTitle);
$document[0].title = titleExp($state.$current.locals.resolve.$$values);
});
And:
...
data: {
pageTitle: "{{resolvedItem.name}} - My Apps"
}
...