使用用户界面路由器设置页面标题

我正在迁移我的 AngularJS 为基础的应用程序使用 ui 路由器,而不是内置的路由。我已经配置了它,如下所示

.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/home');
$stateProvider
.state('home', {
url: '/home',
templateUrl : 'views/home.html',
data : { pageTitle: 'Home' }


})
.state('about', {
url: '/about',
templateUrl : 'views/about.html',
data : { pageTitle: 'About' }
})
});

如何使用 pageTitle 变量动态设置页面的标题?使用内置的路由,我可以做

$rootScope.$on("$routeChangeSuccess", function(currentRoute, previousRoute){
$rootScope.pageTitle = $route.current.data.pageTitle;
});

然后将变量绑定到 HTML 中,如下所示

<title ng-bind="$root.pageTitle"></title>

是否有类似的事件,我可以挂钩到使用 ui 路由器?我注意到有‘ onEnter’和‘ onExit’函数,但它们似乎与每个状态绑定,需要我重复代码为每个状态设置 $rootScope 变量。

63379 次浏览

Use $stateChangeSuccess.

You can put it in a directive:

app.directive('updateTitle', ['$rootScope', '$timeout',
function($rootScope, $timeout) {
return {
link: function(scope, element) {


var listener = function(event, toState) {


var title = 'Default Title';
if (toState.data && toState.data.pageTitle) title = toState.data.pageTitle;


$timeout(function() {
element.text(title);
}, 0, false);
};


$rootScope.$on('$stateChangeSuccess', listener);
}
};
}
]);

And:

<title update-title></title>

Demo: http://run.plnkr.co/8tqvzlCw62Tl7t4j/#/home

Code: http://plnkr.co/edit/XO6RyBPURQFPodoFdYgX?p=preview

Even with $stateChangeSuccess the $timeout has been needed for the history to be correct, at least when I've tested myself.


Edit: Nov 24, 2014 - Declarative approach:

app.directive('title', ['$rootScope', '$timeout',
function($rootScope, $timeout) {
return {
link: function() {


var listener = function(event, toState) {


$timeout(function() {
$rootScope.title = (toState.data && toState.data.pageTitle)
? toState.data.pageTitle
: 'Default title';
});
};


$rootScope.$on('$stateChangeSuccess', listener);
}
};
}
]);

And:

<title>\{\{title}}</title>

Demo: http://run.plnkr.co/d4s3qBikieq8egX7/#/credits

Code: http://plnkr.co/edit/NpzQsxYGofswWQUBGthR?p=preview

You are actually really close with your first answer/question. Add your title as a data object:

.state('home', {
url: '/home',
templateUrl : 'views/home.html',
data : { pageTitle: 'Home' }
})

In your index.html bind the data directly to the page title:

<title data-ng-bind="$state.current.data.pageTitle + ' - Optional text'">Failsafe text</title>

There is a another way of doing this by combining most of the answers here already. I know this is already answered but I wanted to show the way I dynamically change page titles with ui-router.

If you take a look at ui-router sample app, they use the angular .run block to add the $state variable to $rootScope.

// It's very handy to add references to $state and $stateParams to the $rootScope
// so that you can access them from any scope within your applications.
// For example, <li ng-class="{ active: $state.includes('contacts.list') }">
// will set the <li> to active whenever 'contacts.list' or one of its
// decendents is active.


.run([ '$rootScope', '$state', '$stateParams',
function ($rootScope, $state, $stateParams) {
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
}])

With this defined you can then easily dynamically update your page title with what you have posted but modified to use the defined state:

Setup the state the same way:

.state('home', {
url: '/home',
templateUrl : 'views/home.html',
data : { pageTitle: 'Home' }
})

But edit the html a bit...

<title ng-bind="$state.current.data.pageTitle"></title>

I can't say this is any better than the answers before... but was easier for me to understand and implement. Hope this helps someone!

Attaching $state to $rootscope to use anywhere in the app.

app.run(['$rootScope', '$state', '$stateParams',
function ($rootScope,   $state,   $stateParams) {


// It's very handy to add references to $state and $stateParams to the $rootScope
// so that you can access them from any scope within your applications.For example,
// <li ng-class="{ active: $state.includes('contacts.list') }"> will set the <li>
// to active whenever 'contacts.list' or one of its decendents is active.
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
}
]
)
<title ng-bind="$state.current.name + ' - ui-router'">about - ui-router</title>

Why not just:

$window.document.title = 'Title';

UPDATE: Full Directive Code

var DIRECTIVE = 'yourPageTitle';


yourPageTitle.$inject = ['$window'];
function yourPageTitle($window: ng.IWindowService): ng.IDirective {


return {
link: (scope, element, attrs) => {


attrs.$observe(DIRECTIVE, (value: string) => {


$window.document.title = value;
});
}
}
}


directive(DIRECTIVE, yourPageTitle);

Then in every page you would just include this directive:

<section
your-page-title="\{\{'somePage' | translate}}">

The angular-ui-router-title plugin makes it easy to update the page title to a static or dynamic value based on the current state. It correctly works with browser history, too.

I found this way really easy:

  .state('app.staff.client', {
url: '/client/mine',
title: 'My Clients'})

and then in my HTML like this:

<h3>\{\{ $state.current.title }}</h3>

I ended up with this combination of Martin's and tasseKATT's answers - simple and without any template related stuff:

$rootScope.$on("$stateChangeSuccess", function (event, toState) {
$timeout(function () { // Needed to ensure the title is changed *after* the url so that history entries are correct.
$window.document.title = toState.name;
});
});

If you are using ES6, this works just fine :).

class PageTitle {
constructor($compile, $timeout) {
this.restrict = 'A';
this._$compile = $compile;
this.$timeout = $timeout;
}


compile(element) {
return this.link.bind(this);
}


link(scope, element, attrs, controller) {
let defaultTitle = attrs.pageTitle ? attrs.pageTitle : "My Awesome Sauce Site";
let listener = function(event, toState) {
let title = defaultTitle;
if (toState.data && toState.data.title) title = toState.data.title + ' | ' + title;
$('html head title').text(title);
};
scope.$on('$stateChangeStart', listener);
}
}


export function directiveFactory($compile) {
return new PageTitle($compile);
}


directiveFactory.injections = ['$compile', '$timeout'];


export default PageTitle;

Maybe you can try this directive.

https://github.com/afeiship/angular-dynamic-title

Here is the example:

html:

<title dynamic-title>Title</title>


<a href="javascript:;" ui-sref="state1">State1 page</a>
<a href="javascript:;" ui-sref="state2">State2 page</a>

javascript:

var TestModule = angular.module('TestApp', ['ui.router','nx.widget'])
.config(function ($stateProvider, $urlRouterProvider) {
//
// For any unmatched url, redirect to /state1
$urlRouterProvider.otherwise("/state1");
//
// Now set up the states
$stateProvider
.state('state1', {
url: "/state1",
templateUrl: "partials/state1.html",
data:{
pageTitle:'State1 page title11111'
}
})
.state('state2', {
url: "/state2",
templateUrl: "partials/state2.html",data:{
pageTitle:'State2 page title222222'
}
});
})
.controller('MainCtrl', function ($scope) {
console.log('initial ctrl!');
});

Just update window.document.title:

.state('login', {
url: '/login',
templateUrl: "/Login",
controller: "loginCtrl",
onEnter: function($window){$window.document.title = "App Login"; }
})

That way 'ng-app' does not need to move up to the HTML tag and can stay on the body or lower.

I'm using ngMeta, which works well for not only setting page title but descriptions as well. It lets you set a specific title/description for each state, defaults for when a title/description is not specified, as well as default title suffixes (i.e., ' | MySiteName') and author value.

$stateProvider
.state('home', {
url: '/',
templateUrl: 'views/home.html',
controller: 'HomeController',
meta: {
'title': 'Home',
'titleSuffix': ' | MySiteName',
'description': 'This is my home page description lorem ipsum.'
},
})

$stateChangeSuccess is now deprecated in UI-Router 1.x and disabled by default. You'll now need to use the new $transition service.

A solution isn't too difficult once you understand how $transition works. I got some help from @troig in understanding it all. Here's what I came up with for updating the title.

Put this in your Angular 1.6 application. Note that I'm using ECMAScript 6 syntax; if you are not, you'll need e.g. to change ABC0 to var.

.run(function($transitions, $window) {
$transitions.onSuccess({}, (transition) => {
let title = transition.to().title;
if (title) {
if (title instanceof Function) {
title = title.call(transition.to(), transition.params());
}
$window.document.title = title;
}
});

Then just add a title string to your state:

$stateProvider.state({
name: "foo",
url: "/foo",
template: "<foo-widget layout='row'/>",
title: "Foo Page""
});

That will make the words "Foo Page" show up in the title. (If a state has no title, the page title will not be updated. It would be a simple thing to update the code above to provide a default title if a state does not indicate one.)

The code also allows you to use a function for title. The this used to call the function will be the state itself, and the one argument will be the state parameters, like this example:

$stateProvider.state({
name: "bar",
url: "/bar/{code}",
template: "<bar-widget code='\{\{code}}' layout='row'/>",
title: function(params) {
return `Bar Code ${params.code}`;
}
});

For the URL path /bar/code/123 that would show "Bar Code 123" as the page title. Note that I'm using ECMAScript 6 syntax to format the string and extract params.code.

It would be nice if someone who had the time would put something like this into a directive and publish it for everyone to use.

For Updated UI-Router 1.0.0+ versions, (https://ui-router.github.io/guide/ng1/migrate-to-1_0)

Refer to following code

app.directive('pageTitle', [
'$rootScope',
'$timeout',
'$transitions',
function($rootScope, $timeout,$transitions) {
return {
restrict: 'A',
link: function() {
var listener = function($transitions) {
var default_title = "DEFAULT_TITLE";
$timeout(function() {
$rootScope.page_title = ($transitions.$to().data && $transitions.$to().data.pageTitle)
? default_title + ' - ' + $transitions.$to().data.pageTitle : default_title;
                    	

                        

});
};
$transitions.onSuccess({ }, listener);
}
}
}
])

Add following to your index.html:

<title page-title ng-bind="page_title"></title>