Встановити заголовок сторінки за допомогою UI-маршрутизатора


101

Я мігрую свою програму на основі 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 для кожного стану.


3
Існує подія $ stateChangeSuccess.
Джеррад

Відповіді:


108

Використовуйте $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що було потрібно, щоб історія була правильною, принаймні, коли я перевіряв себе.


Редагувати: 24 листопада 2014 р. - Декларативний підхід:

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


Супер чудово. Це не могло бути легше
Меттью Харвуд

3
Цей приклад також не працює належним чином з історією (принаймні в Chrome 37). Якщо ви переходите між різними станами, то перегляньте свою історію, заголовок предмета історії буде значенням попередньої сторінки. Якщо ви перейдете на сторінку1 -> page2 -> page3, тоді перегляньте історію, URL2 сторінки url буде відповідати заголовку page1.
jkjustjoshing

2
Власне, це не зовсім точно. Заголовок сторінки змінюється до того, як зміниться хеш URL-адреси, тому браузер вважає, що новий заголовок призначений для старої сторінки. Потім історія кнопок "назад" відключена. Обгортання element.text(title)дзвінка в $ timeout працювало для мене. Редагування оригінальної публікації.
jkjustjoshing

3
Це не спрацює, якщо заголовок повинен бути динамічним на основі деяких параметрів URL.
Кушагра Гур

10
@KushagraGour Якщо потреби титульних бути динамічними на основі $ stateParams, ви можете використовувати функцію resolveдля його генерації, а потім отримати доступ до «дозволеному» значенням в протягом $ stateChangeSuccess події з: $state.$current.locals.resolve.$$values.NAME_OF_RESOLVE_FUNCTION.
Клаус Конрад

91

Є ще один спосіб зробити це, поєднуючи більшість відповідей тут уже. Я знаю, що на це вже відповіли, але я хотів показати спосіб динамічно змінювати заголовки сторінок за допомогою ui-роутера.

Якщо ви подивитеся на зразок програми ui-router , вони використовують кутовий блок .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>

Я не можу сказати, що це краще, ніж відповіді раніше ... але мені було легше зрозуміти та реалізувати. Сподіваюся, це комусь допоможе!


3
Більш декларативно, ніж прийнята відповідь. Мені подобається це!
Endy Tjahjono

3
Не впевнений, чи хотілося б мені весь об’єкт $ range у $ rootScope лише для назви сторінки ...
Jesús Carrera

Я не впевнений, куди посилається на об’єкт $ range @ JesúsCarrera
cwbutler

Джерело, вибачте, я маю на увазі об’єкт $ state
Хесус Каррера

4
просто для підтвердження github.com/angular-ui/ui-router/wiki/Quick-Reference також рекомендує налаштування $stateта $stateParamsвключення $rootScopeзсередини run.
Марк Петерсон

17

Кутовий-UI-маршрутизатор титульний плагін дозволяє легко оновити заголовок сторінки до статичному або динамічному значенням , засноване на поточний стан. Він правильно працює і з історією браузера.


Це, здається, найкраще рішення в майбутньому. Я помітив кілька невідповідностей історії історії браузера, використовуючи деякі інші рішення на цій сторінці.

Заголовок angular-ui-router, здається, є найкращим рішенням. Найбільше це без проблем! Дякую Степан.
Даріо

Це дуже крихітний вихідний файл.
Тайлер Колліє

15

$stateChangeSuccessтепер застаріло в UI-Router 1.x і відключено за замовчуванням. Тепер вам потрібно буде використовувати нове$transition послугою.

Рішення не надто складне, як тільки ви зрозумієте, як $transitionпрацює. Я отримав допомогу від @troig, щоб зрозуміти це все. Ось що я придумав для оновлення заголовка.

Помістіть це у своїй програмі 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}`;
    }
});

Для URL-адреси, /bar/code/123який відображав би "Штрих-код 123" як заголовок сторінки. Зауважте, що я використовую синтаксис ECMAScript 6 для форматування рядка та витягу params.code.

Було б добре, якби хтось, хто мав час, вклав щось подібне в директиву і опублікував це для всіх.


Використовуйте dataоб'єкт для користувацьких ключів. titleне існує в StateDeclarationінтерфейсі.
Гауї

5

Додавання $ state до $ rootcope для використання в будь-якому місці програми.

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>


1
Поєднав це з додаванням заголовка до кожного штату. Працює ідеально.
WCByrne


5

Просто оновіть window.document.title:

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

Таким чином, "ng-app" не потрібно переходити до тегу HTML і може залишатися на тілі чи нижче.


1
Чому це не найкраща відповідь? = /
rex

3

Я використовую 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.'
    },
  })

2

Ви насправді дуже близькі зі своєю першою відповіддю / питанням. Додайте назву як об’єкт даних:

.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>

1

Я закінчив це поєднання відповідей Мартіна та 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; 
   });
});

якщо немає будь-яких матеріалів, пов’язаних із шаблоном, як би новий розробник знав, як змінюється заголовок, не запитуючи, як це змінюється?
ton.yeung

якщо ви використовуєте $ window.document.title $ timeout, марний. Я слідую за цим хакішем просто для того, щоб позбутися $ timeout та циклу переваривання $ :)
Whisher

1

Чому б не просто:

$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}}">

потенційно може бути дуже важко з'ясувати, чому / як змінюється заголовок для кожного, хто успадковує кодову базу
ton.yeung

Чому це важко буде з'ясувати? Цей фрагмент повинен бути ініційований з директиви, скажімо, your-page-titile = "{{'pageTitle' | перекладати}}. Ця директива буде включена в перший елемент кожної сторінки. Приємно і декларативно зрозуміло.
Мартін

о, із редагуванням, я бачу, що ти маєш на увазі зараз. що я мав на увазі, що один вкладиш потенційно можна розмістити де завгодно, $ rootcope, директива тощо ...
ton.yeung

0

Якщо ви використовуєте 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;

0

Можливо, ви можете спробувати цю директиву.

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

0

Для оновлених версій 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:

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.