I am using ng-view to include AngularJS partial views, and I want to update the page title and h1 header tags based on the included view. These are out of scope of the parti
Here is an adapted solution that works for me which doesn't require injection of $rootScope into controllers for setting resource specific page titles.
In the master template:
<html data-ng-app="myApp">
<head>
<title data-ng-bind="page.title"></title>
...
In the routing config:
$routeProvider.when('/products', {
title: 'Products',
templateUrl: '/partials/products.list.html',
controller: 'ProductsController'
});
$routeProvider.when('/products/:id', {
templateUrl: '/partials/products.detail.html',
controller: 'ProductController'
});
And in the run block:
myApp.run(['$rootScope', function($rootScope) {
$rootScope.page = {
setTitle: function(title) {
this.title = title + ' | Site Name';
}
}
$rootScope.$on('$routeChangeSuccess', function(event, current, previous) {
$rootScope.page.setTitle(current.$$route.title || 'Default Title');
});
}]);
Finally in the controller:
function ProductController($scope) {
//Load product or use resolve in routing
$scope.page.setTitle($scope.product.name);
}
The module angularjs-viewhead shows a mechanism to set the title on a per-view basis using only a custom directive.
It can either be applied to an existing view element whose content is already the view title:
<h2 view-title>About This Site</h2>
...or it can be used as a standalone element, in which case the element will be invisible in the rendered document and will only be used to set the view title:
<view-title>About This Site</view-title>
The content of this directive is made available in the root scope as viewTitle
, so it can be used on the title element just like any other variable:
<title ng-bind-template="{{viewTitle}} - My Site">My Site</title>
It can also be used in any other spot that can "see" the root scope. For example:
<h1>{{viewTitle}}</h1>
This solution allows the title to be set via the same mechanism that is used to control the rest of the presentation: AngularJS templates. This avoids the need to clutter controllers with this presentational logic. The controller needs to make available any data that will be used to inform the title, but the template makes the final determination on how to present it, and can use expression interpolation and filters to bind to scope data as normal.
(Disclaimer: I am the author of this module, but I'm referencing it here only in the hope that it will help someone else to solve this problem.)
Simple and dirty way using $rootScope
:
<html ng-app="project">
<head>
<title ng-bind="title">Placeholder title</title>
In your controllers, when you have the data necessary to create the title, do:
$rootScope.title = 'Page X'
Note that you can also set the title directly with javascript, i.e.,
$window.document.title = someTitleYouCreated;
This does not have data binding, but it suffices when putting ng-app
in the <html>
tag is problematic. (For example, using JSP templates where <head>
is defined in exactly one place, yet you have more than one app.)
I just discovered a nice way to set your page title if you're using routing:
JavaScript:
var myApp = angular.module('myApp', ['ngResource'])
myApp.config(
['$routeProvider', function($routeProvider) {
$routeProvider.when('/', {
title: 'Home',
templateUrl: '/Assets/Views/Home.html',
controller: 'HomeController'
});
$routeProvider.when('/Product/:id', {
title: 'Product',
templateUrl: '/Assets/Views/Product.html',
controller: 'ProductController'
});
}]);
myApp.run(['$rootScope', function($rootScope) {
$rootScope.$on('$routeChangeSuccess', function (event, current, previous) {
$rootScope.title = current.$$route.title;
});
}]);
HTML:
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<title ng-bind="'myApp — ' + title">myApp</title>
...
Edit: using the ng-bind
attribute instead of curlies {{}}
so they don't show on load
Here is another approach that hasn't been mentioned by the others here (as of this writing).
You can use custom events like so:
// your index.html template
<html ng-app="app">
<head>
<title ng-bind="pageTitle">My App</title>
// your main app controller that is declared on the <html> element
app.controller('AppController', function($scope) {
$scope.$on('title-updated', function(newTitle) {
$scope.pageTitle = newTitle;
});
});
// some controller somewhere deep inside your app
mySubmodule.controller('SomeController', function($scope, dynamicService) {
$scope.$emit('title-updated', dynamicService.title);
});
This approach has the advantage of not requiring extra services to be written and then injected into every controller that needs to set the title, and also doesn't (ab)use the $rootScope
. It also allows you to set a dynamic title (as in the code example), which is not possible using custom data attributes on the router's config object (as far as I know at least).