How to dynamically change header based on AngularJS partial view?
Solution 1
You could define controller at the <html>
level.
<html ng-app="app" ng-controller="titleCtrl">
<head>
<title>{{ Page.title() }}</title>
...
You create service: Page
and modify from controllers.
myModule.factory('Page', function() {
var title = 'default';
return {
title: function() { return title; },
setTitle: function(newTitle) { title = newTitle }
};
});
Inject Page
and Call 'Page.setTitle()' from controllers.
Here is the concrete example: http://plnkr.co/edit/0e7T6l
Solution 2
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
Solution 3
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.)
Solution 4
Declaring ng-app
on the html
element provides root scope for both the head
and body
.
Therefore in your controller inject $rootScope
and set a header property on this:
function Test1Ctrl($rootScope, $scope, $http) { $rootScope.header = "Test 1"; }
function Test2Ctrl($rootScope, $scope, $http) { $rootScope.header = "Test 2"; }
and in your page:
<title ng-bind="header"></title>
Solution 5
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.)
Michael Low
Updated on May 05, 2020Comments
-
Michael Low almost 4 years
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 partial view controllers though, and so I can't figure out how to bind them to data set in the controllers.
If it was ASP.NET MVC you could use @ViewBag to do this, but I don't know the equivalent in AngularJS. I've searched about shared services, events etc but still can't get it working. Any way to modify my example so it works would be much appreciated.
My HTML:
<html data-ng-app="myModule"> <head> <!-- include js files --> <title><!-- should changed when ng-view changes --></title> </head> <body> <h1><!-- should changed when ng-view changes --></h1> <div data-ng-view></div> </body> </html>
My JavaScript:
var myModule = angular.module('myModule', []); myModule.config(['$routeProvider', function($routeProvider) { $routeProvider. when('/test1', {templateUrl: 'test1.html', controller: Test1Ctrl}). when('/test2', {templateUrl: 'test2.html', controller: Test2Ctrl}). otherwise({redirectTo: '/test1'}); }]); function Test1Ctrl($scope, $http) { $scope.header = "Test 1"; /* ^ how can I put this in title and h1 */ } function Test2Ctrl($scope, $http) { $scope.header = "Test 2"; }
-
Narretz over 11 yearsDoes this work in Firefox for you? I get "Error: No module: ui" in the console
-
superjos about 11 yearsuhmm... I'm not sure if placing a service straight into the $scope is considered nice in the AngularJS architecture. Maybe it could be better to put in $scope a Controller function, and then let this function query the service.
-
Eric Drechsel almost 11 yearsthis solution is full of win! it clarifies for me how to manage state at the page level while using ng-view and ngRouter. Thanks!
-
Eric Drechsel almost 11 yearsThis doesn't allow for dynamic setting of the title based on $scope variables.
-
jkoreska almost 11 years@EricDrechsel You can set $rootScope.title from other places in your app, this logic only changes it on $routeChangeSuccess.
-
Arthur Frankel almost 11 yearsThis example was terrific. I have one followup though, on initial load you can see the {{ Page.title() }} text in the title (very quickly). I don't think you can use ng-cloak since it's not in the body. Any suggestions to avoid this?
-
Eric Drechsel almost 11 yearsright, but your example doesn't show how to change the title on $routeChangeSuccess parameterized by $scope variables, which @tosh's example using a Page service does. So you can set
title = "Blog"
but nottitle = '{{"Blog post " + post.title}}'
. -
Chris Sattinger almost 11 yearsIts actually double $ in the property name : current.$$route.title maybe stackoverflow swallowed the other $
-
Eldelshell almost 11 years@felix you can access title like
current.title
also -
JeremyWeir almost 11 yearsI just posted another approach for when it isn't possible to have the title tag in an ngapp... stackoverflow.com/a/17751833/45767
-
Lukus almost 11 yearsNice direct approach that reminds you of the obvious. I simple wrapped this in my 'ui' service as setTitle() function, injected the $window dependency, and since my service is in all my controllers, now can call ui.setTitle('example title') wherever I need to in any controller.
-
Maarten over 10 yearsThis was the only way to get it to work on Internet Explorer for me, the other methods worked on other browsers though
-
Pius Uzamere over 10 years@ArthurFrankel Just use ng-bind (e.g. ng-bind="Page.title()")
-
sharat87 over 10 yearsOr use the
ng-bind-template
directive and you can use the curly braces syntax. From the docs, it looks like this attribute was added for setting the document title. -
david.sansay over 10 years$rootScope.title = current.$route.title; without doble $$
-
rob over 10 yearsAs Maarten mentioned this is the only approach that works in ie7 and ie8
-
Tyler Forsythe about 10 yearsI just upgraded my Angular version several versions (1.0.5 to 1.2.7) and this broke me for in my code. I was using
current.$route
in the old code and it was working. With the upgrade, the double $ on route is needed.current.$$route
-
redben about 10 yearsAmazing how people can't step back and see how easily this thing can be done without scopes and factories
-
ViniciusPires about 10 yearsUseful and upvoted. The only problem is when you have up to 20 or more controllers, the code doesn't scale well, and holds tight the responsibility of changing the title to the controllers. See this example for a more scalable solution: coderwall.com/p/vcfo4q
-
Leonard Teo about 10 yearsUnbelievable. This was far simpler than all the shenanigans others were mentioning. Thanks!
-
Dmitri Algazin almost 10 yearsor we can specify controller in title tag, no need for global controller on html header: <title ng-controller="titleCtrl">{{ Page.title() }}</title>
-
Dmitri Algazin almost 10 yearssmall problem, when you refresh a page, in your tab name you see '{{ title }}' and after page was rendered, you see 'Some Title' only. solution with factory does not have that behavior
-
Admin almost 10 yearsI must admit, although I up-voted this answer, using the
ng-bind-template={{pageTitle}}
attribute on thetitle
element is much cleaner. It prevents the browser title showing "{{pageTitle}}" until the app has loaded. -
Chad Johnson almost 10 yearsThis is great, BUT, the only thing is that $$route is considered to be "private."
-
Nicolas Janel almost 10 yearsbest answer on my opinion. To have a controller on ng-app level as described in accepted answer is useless in this case.
-
RachelD almost 10 yearsThank you, your example made me realize that I wasn't using services to their full potential.
-
CIF almost 10 yearsPage.title() didn't work for me, but instead i just used $scope.pageTitle = Page.title(); and used {{pageTitle}} in template. Works & thanks!
-
blockloop almost 10 yearscurly brackets don't show if you're using ng-cloak
-
DDA almost 10 yearsI personally prefer to set the title on the
$rootScope
instead of creating an additional controller. -
amit bakle almost 10 yearsMy opinion, don't use this. You are mixing data(information) in views(presentation). Later on it will be very difficult to find title sources scattered all over your HTML links that may be present at various places in the view..
-
Martin Wawrusch over 9 yearsCan't believe this solution hasn't been upvoted more. Most of the other ones are really bad design choices.
-
tedwards947 over 9 yearsi love how lightweight this solution is, and it avoids using $$ properties
-
Leon Gaban over 9 yearsHow do you install $locationProvider, when I paste your exact code it errors out
-
Admin over 9 yearsI can't get this to work.. I've got
ui-router
updating URL and content based on my state and I get no errors or warnings, but I can't seem to access any part of the state config object through$state.current.[...]
. What version ofui-router
did you use to do this? -
Admin over 9 yearsMy "Runtime Config" edit to the answer solves the problem I mentioned in my comment above. :) I'm open to ideas if there's a better way to do this though.
-
special0ne over 9 yearsThe accepted answer adds unnecessary complication and risks. This version makes it as simple as setting a variable.
-
Graham Fowles over 9 yearsI implemented this solution for title and description and it works fine except when i check my page on woorank it shows as title missing and description is {{Page.getDescription()}}. Does this mean that search engines will also get the same???
-
Natus Drew over 9 yearsThis answer worked the best. The most elegant solution.
-
johngeorgewright over 9 years@EricDrechsel use the
resolve
option when trying to use dynamic variables.resolve: { blog: function($route, Blog) { $route.current.$$route.title = /*etc*/ } }
-
David Anderson over 9 yearsAnyone having troubles getting this to work in Angular 1.3.4? The event fires, the values are correct, but the binding never updates the text. In my case, I have the binding on a span tag.
-
Michael J. Calkins over 9 yearsIf you're dead set on using $rootScope I'd at least extract this out to a service so you don't have $rootScope in your controller.
-
jbltx over 9 yearsIn the answser when can see
'/Product/:id'
. Is there any way to have the:id
value with this method ? I triedtitle: function(params){return params.id;}
but doesn't work... Maybe usingresolve
? -
remarsh over 9 yearsI want to use this solution but I am curious what the advantages of using it are over
document.title = "App"
; -
Nate Barbettini over 9 yearsAgreed, this should be the top solution. I like this a lot better than declaring a controller at the page level for setting the title. FYI: using this with Angular v1.3.2 and angular-route-segment v1.3.3 and it's working like a charm.
-
StackOverflowUser over 9 yearsI was able to do this successfully from the controller after retrieving data from a web api call, and it was the only technique that worked for me - but it only worked when I did not begin the line with the dollar sign. I had to use window.document.title rather than $window.document.title.
-
broc.seib over 9 yearsUsing plain 'window' is fine -- that's directly acting on the DOM. '$window' is an angular thing, and you have to inject it to use it. Either way will work.
-
Mark Amery over 9 yearsBecause the title only gets updated upon actually clicking a link, this doesn't set the title correctly when the user first lands on a page, or when the user refreshes.
-
Kristo Aun about 9 yearsThe title set in ProductController ($scope.page.setTitle) is being overridden by $rootScope.$on('$routeChangeSuccess'. Setting a default title in $rootScope.$on('$routeChangeStart' is safer in this respect.
-
mikhail-t about 9 years@mr-hash: here is small adjustment I suggest, perfect for existing angular projects with many routes, but without titles. It'll generate title from controller's name, if no title is defined on the route:
$rootScope.page.setTitle(current.$$route.title || current.$$route.controller.replace('Ctrl', ''));
-
mikhail-t about 9 yearsI personally think solution by Mr. Hash (here on the same question) is much better one: stackoverflow.com/a/17898250/448816 It allows for page title to be generated dynamically based on route and insert custom title from controller if needed.
-
Richard de Wit about 9 yearsThe
$location
dependency isn't used/necessary. (Tested it, doesn't need it) -
Rober about 9 yearsDoes your solution work in this scenario? stackoverflow.com/questions/28760904/…
-
TechCrunch about 9 yearsGood enough for a small application with handful partial views.
-
James Harrington about 9 yearsI like to use this method but i put a fallback in the ng-bind. something like ` ng-bind="title || 'My Cool Site' "`
-
sidonaldson about 9 years$window is only a wrapper so you can integrate and manipulate it for automated testing purposed. If you just want to set something do it directly :)
-
jkoreska about 9 yearsI endorse this solution ;)
-
Tomino about 9 yearsThis solution works great... until you use
.otherwise({redirectTo: '/'});
. In this case title is empty and title value from'/'
route is not used. Does anybody knows solution for this case? -
Mark Pieszak - Trilon.io almost 9 years@Tomino Just do this within your .run code
$rootScope.title = current.$$route !== undefined ? current.$$route.title : 'your home page title here';
That'll make sure there even is a $$route before applying it. Fixes the issue! -
Martin Atkins almost 9 yearsI wrote a bit more about angularjs-viewhead and another related idea here on my blog: apparently.me.uk/angularjs-view-specific-sidebars
-
z0r almost 9 yearsI came up with something similar. By far the most intuitive to use, and doesn't require putting a controller on
html
. In my directive I also inject an optionalpageTitlePrefix
constant. -
Henrik Stenbæk over 8 yearsremember to sanitize output like this:
this.title = title.replace('<', '<').replace('>', '>').replace(' & ', ' & ') + ' | Site Name';
-
Faradox over 8 yearsTitle sets only 'default'. why?
-
Faradox over 8 yearsinstead
{{title}}
useng-bind='title'
-
Taiwei Tuan over 8 yearsYour solution resolved my issue of disappearing of title when reload page(F5). Awesome answer!
-
Seth over 8 yearsAgree with @Faradox... using
ng-bind
prevents the pre-interpolated syntax from displaying before the title actually evaluates. +100 -
sgimeno over 8 years$document[0].title should work as well. Using angular document and window wrappers is nice for latter testing, prevent jshint warnings, etc.
-
GraehamF over 8 yearsthis does not work for me and 'title' is not found in the API docs - is this still supported?
-
anre over 8 yearsIf reusing the same view at top-level and at sub-level view, one can still use view-title with a ng-if, e.g.: <h4 ng-if="$state.includes('some-state')" view-title>Details for {{...}}</h4> <h4 ng-if="!$state.includes('some-state')">Details for {{...}}</h4>
-
Andy over 8 yearsI got undefined error so I changed the last bit to: $rootScope.page.title = current.$$route ? current.$$route.title + ' | Site Name' : 'Site Name';
-
Eolis about 8 yearsthe answer I ended up using; though I use a single factory to match all the urls and set the title/description as to not require $rootScope in all controllers
-
Alex Boisselle about 8 yearsThis is absolutely the best way to do it. If your app takes any time to load at all, the other solution will show the pre-parsed {{ var }} in the tab before angular runs. Therefore using ng-bind is the best solution. Nicely done.
-
Alex Boisselle about 8 yearsWhat works well for me: <title ng-bind="'My App | ' + LANG['MENU_' + $state.current.name.replace('dashboard.', '').toUpperCase()]">My App</title>
-
0xtuytuy about 8 yearsdefinitly the best answer ! so light weight and straight forward.
-
Asad Ali Khan almost 8 yearsNice solutions.... Will this be indexed in google also... as "Fetch as google" shows <title ng-bind="'myApp — ' + title">myApp</title> in its "Downloaded HTTP response:" ...
-
Warren over 7 yearsSetting title from the controller (top level) can still be done using $scope.$parent.title = 'My Dynamic Title'; (if you're using angular routing)
-
MrBoJangles over 7 yearsI had to set the title inside my controller with a category name. I just set the title after I had the value, one line of code, done.
-
MrBoJangles over 7 years"This does not have binding,..." I'd be curious as to the scenario where title binding would need to be two-way. I'm sure there is a scenario, I just don't know what it would be. Perhaps candybox 3...
-
broc.seib over 7 yearsI mean one-way binding, i.e., assign a value to a variable and it magically changes the window title.
-
Cengkuru Michael about 7 yearsif you are using stateProvider you could do this $rootScope.$on('$stateChangeSuccess', function (ev, to, toParams, from, fromParams) { $rootScope.title = to.title; });
-
Abiel Muren about 7 yearsThis was most straightforward solution, the best solution from my humble point of view.
-
Jens Alenius about 7 yearsI would have done it like this if it was not for the language translation in my webapp
-
LatentDenis about 7 yearsWould this be possible (but most importantly, secure) to do with ngStorage?
-
Robycool almost 7 yearsSo clean and elegant ! The only solution where you can have thousands of different titles that will automatically sync when changing languages, users, etc. without needing to bother about it.
-
Etherealm over 6 yearswould this affect the seo in anyway ?
-
sashaegorov over 6 yearsThe most useful way for existing apps.
-
Saurabh Solanki over 5 yearsone of the simpleset way
-
Admin over 4 yearsExactly what I was looking for! Thanks