使用 UI-Router 设置页面标题

Posted

技术标签:

【中文标题】使用 UI-Router 设置页面标题【英文标题】:Set Page title using UI-Router 【发布时间】:2014-07-11 21:22:43 【问题描述】:

我正在迁移基于 AngularJS 的应用程序以使用 ui-router 而不是内置路由。我已将其配置如下所示

.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-router 挂钩?我注意到有 'onEnter' 和 'onExit' 函数,但它们似乎与每个状态相关联,并且需要我重复代码来为每个状态设置 $rootScope 变量。

【问题讨论】:

有一个 $stateChangeSuccess 事件。 【参考方案1】:

使用$stateChangeSuccess

你可以把它放在一个指令中:

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);
      
    ;
  
]);

还有:

<title update-title></title>

演示: http://run.plnkr.co/8tqvzlCw62Tl7t4j/#/home

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

即使使用$stateChangeSuccess,也需要$timeout 才能使历史记录正确,至少在我测试过自己时是这样。


编辑:2014 年 11 月 24 日 - 声明性方法:

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);
      
    ;
  
]);

还有:

<title>title</title>

演示: http://run.plnkr.co/d4s3qBikieq8egX7/#/credits

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

【讨论】:

超级棒。这再简单不过了 这个例子也不能正常使用历史记录(至少在 Chrome 37 中)。如果您在各种状态之间切换,那么查看您的历史记录,历史记录项的标题将是上一页的值。如果你去 page1 -> page2 -> page3,然后查看历史,page2 的 url 将与 page1 的标题匹配。 实际上,这不太准确。页面标题在 URL 哈希更改之前更改,因此浏览器认为新标题是针对旧页面的。然后后退按钮历史记录关闭 1 页。将 element.text(title) 调用包装在 $timeout 中对我有用。编辑原始帖子。 如果标题需要基于某些 url 参数是动态的,这将不起作用。 @KushagraGour 如果标题需要基于 $stateParams 动态,您可以使用 resolve 中的函数来生成它,然后在 $stateChangeSuccess 事件期间访问“已解决”值: $state.$current.locals.resolve.$$values.NAME_OF_RESOLVE_FUNCTION.【参考方案2】:

通过结合这里的大部分答案,还有另一种方法可以做到这一点。我知道这已经得到解答,但我想展示我使用 ui-router 动态更改页面标题的方式。

如果您查看 ui-router sample app,他们使用角度 .run 块将 $state 变量添加到 $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;
])

有了这个定义,您就可以轻松地使用您发布但修改为使用定义状态的内容动态更新您的页面标题:

以同样的方式设置状态:

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

但是稍微编辑一下html...

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

我不能说这比以前的答案更好......但我更容易理解和实施。希望这对某人有帮助!

【讨论】:

比接受的答案更具声明性。我喜欢这个! 不确定我是否希望 $rootScope 中的整个 $scope 对象仅用于页面标题... 我不确定 $scope 对象在哪里被引用@JesúsCarrera Ups,对不起,我的意思是 $state 对象 只是为了确认github.com/angular-ui/ui-router/wiki/Quick-Reference 还建议在run 内设置$state$stateParams【参考方案3】:

angular-ui-router-title 插件可以根据当前状态轻松地将页面标题更新为 staticdynamic 值。它也可以正确处理浏览器历史记录。

【讨论】:

这似乎是最好的解决方案。我注意到使用此页面上的其他一些解决方案时浏览器历史记录存在一些不一致。 angular-ui-router-title 似乎是最好的解决方案。最重要的是它是无忧无虑的!谢谢斯蒂芬。 这是一个非常小的源文件。【参考方案4】:

$stateChangeSuccess 现在是deprecated in UI-Router 1.x 并且默认禁用。您现在需要使用新的$transition 服务。

一旦您了解了$transition 的工作原理,解决方案就不难了。我从@troig 那里得到了一些help 来理解这一切。这是我更新标题的想法。

把它放在你的 Angular 1.6 应用程序中。 请注意,我使用的是 ECMAScript 6 语法;如果你不是,你将需要例如将let 更改为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;
        
    );

然后只需将title 字符串添加到您的状态:

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

这将使“Foo Page”字样出现在标题中。 (如果状态没有标题,则页面标题不会更新。如果状态没有指示,更新上面的代码以提供默认标题是一件简单的事情。)

代码还允许您使用title 的函数。用于调用函数的this 将是状态本身,并且一个参数将是状态参数,例如以下示例:

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

对于将“条形码 123”显示为页面标题的 URL 路径 /bar/code/123请注意,我使用 ECMAScript 6 语法来格式化字符串并提取 params.code

如果有时间的人将这样的内容放入指令中并发布以供所有人使用,那就太好了。

【讨论】:

使用 data 对象作为自定义键。 titleStateDeclaration 接口上不存在。【参考方案5】:

将 $state 附加到 $rootscope 以在应用程序的任何位置使用。

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;
    
  ]
)
&lt;title ng-bind="$state.current.name + ' - ui-router'"&gt;about - ui-router&lt;/title&gt;

【讨论】:

将此与为每个州添加标题相结合。完美运行。【参考方案6】:

我发现this way 真的很容易:

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

然后在我的 HTML 中像这样:

<h3> $state.current.title </h3>

【讨论】:

【参考方案7】:

只需更新 window.document.title:

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

这样 'ng-app' 不需要向上移动到 HTML 标记并且可以停留在 body 或更低。

【讨论】:

为什么这不是最佳答案? =/【参考方案8】:

我正在使用ngMeta,它不仅适用于设置页面标题,还适用于描述。它允许您为每个州设置特定的标题/描述、未指定标题/描述时的默认值,以及默认的标题后缀(即“| MySiteName”)和作者值。

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

【讨论】:

【参考方案9】:

您实际上非常接近您的第一个答案/问题。将您的标题添加为数据对象:

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

在您的 index.html 中将数据直接绑定到页面标题:

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

【讨论】:

【参考方案10】:

我最终得到了 Martin 和 tasseKATT 答案的组合 - 简单且没有任何与模板相关的内容:

$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; 
   );
);

【讨论】:

如果没有任何与模板相关的东西,新开发者如何知道标题是如何更改的而不询问它是如何更改的? 如果你使用 $window.document.title $timeout 是没用的。我遵循这个 hackish 只是为了摆脱 $timeout 和 $digest 循环:)【参考方案11】:

为什么不只是:

$window.document.title = 'Title';

更新:完整指令代码

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);

然后在每个页面中,您只需包含此指令:

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

【讨论】:

对于继承代码库的任何人来说,可能很难找出标题为何/如何发生变化 为什么很难找到?此片段应由指令触发,例如 your-page-titile=" 'pageTitle' | translate 。该指令将包含在每个页面的第一个元素中。很好,声明性清晰。 哦,有了编辑,我明白你现在的意思了。我的意思是,一个班轮可能被放在任何地方,$rootscope、指令等......【参考方案12】:

如果你使用的是 ES6,这很好:)。

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;

【讨论】:

【参考方案13】:

也许你可以试试这个指令。

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

示例如下:

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!');
    );

【讨论】:

【参考方案14】:

对于更新的 UI-Router 1.0.0+ 版本, (https://ui-router.github.io/guide/ng1/migrate-to-1_0)

参考以下代码

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);
            
        
    
])

将以下内容添加到您的 index.html:

&lt;title page-title ng-bind="page_title"&gt;&lt;/title&gt;

【讨论】:

以上是关于使用 UI-Router 设置页面标题的主要内容,如果未能解决你的问题,请参考以下文章

使用 Angular ui-router 设置 URL 查询参数而不改变状态

在每个页面中使用嵌套视图时如何使用 ui-router 进行页面重定向?

(AngularJS) 设置 UI-Router 后注入不起作用

使用 AngularJS 中的 UI-Router 将状态重定向到默认子状态

使用 ui-router 创建路由参数

使用 locationProvider 时,刷新浏览器网页不会在 ui-router 中加载