Как динамически изменять заголовок на основе частичного представления AngularJS?

Я использую ng-view для включения частичных представлений AngularJS, и я хочу обновить заголовок страницы и теги заголовка h1 на основе включенного представления. Однако они выходят за рамки контроллеров частичного представления, поэтому я не могу понять, как связать их с набором данных в контроллерах.

Если бы это был ASP.NET MVC, вы могли бы использовать @ViewBag для этого, но я не знаю эквивалента в AngularJS. Я искал общие службы, события и т. Д., Но все еще не могу заставить его работать. Любой способ изменить мой пример, чтобы он работал, был бы очень признателен.

Мой 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>

Мой 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"; }

person Michael Low    schedule 20.09.2012    source источник
comment
Этот комментарий может быть запоздалым, но я хочу добавить. cssfacts.com/simple-dynamic-meta-tags-in-angularjs Это может быть полезно для установки динамических метаданных. Вы просто измените мета-переменную $ rootScope.   -  person Kamuran Sönecek    schedule 07.04.2016


Ответы (22)


Вы можете определить контроллер на уровне <html>.

 <html ng-app="app" ng-controller="titleCtrl">
   <head>
     <title>{{ Page.title() }}</title>
 ...

Вы создаете сервис: Page и модифицируете его из контроллеров.

myModule.factory('Page', function() {
   var title = 'default';
   return {
     title: function() { return title; },
     setTitle: function(newTitle) { title = newTitle }
   };
});

Ввести Page и вызвать 'Page.setTitle ()' из контроллеров.

Вот конкретный пример: http://plnkr.co/edit/0e7T6l

person Tosh    schedule 20.09.2012
comment
Это работает в Firefox для вас? Я получаю сообщение об ошибке: нет модуля: пользовательский интерфейс в консоли - person Narretz; 20.09.2012
comment
ммм ... Я не уверен, что размещение сервиса прямо в $ scope считается хорошим в архитектуре AngularJS. Возможно, было бы лучше поместить в $ scope функцию контроллера, а затем позволить этой функции запрашивать службу. - person superjos; 06.03.2013
comment
это решение полно победы! он разъясняет мне, как управлять состоянием на уровне страницы при использовании ng-view и ngRouter. Спасибо! - person Eric Drechsel; 16.05.2013
comment
Этот пример был потрясающим. Однако у меня есть одно наблюдение: при начальной загрузке вы можете увидеть текст {{Page.title ()}} в заголовке (очень быстро). Я не думаю, что вы можете использовать ng-cloak, так как его нет в теле. Есть предложения, как этого избежать? - person Arthur Frankel; 17.05.2013
comment
Я только что опубликовал другой подход, когда невозможно иметь тег заголовка в ngapp ... stackoverflow.com/a/17751833 / 45767 - person JeremyWeir; 19.07.2013
comment
@ArthurFrankel Просто используйте ng-bind (например, ng-bind = Page.title ()) - person Pius Uzamere; 14.08.2013
comment
Полезно и одобрено. Единственная проблема заключается в том, что когда у вас есть до 20 или более контроллеров, код плохо масштабируется и несет ответственность за изменение заголовка контроллеров. См. Этот пример для более масштабируемого решения: coderwall.com/p/vcfo4q - person ViniciusPires; 31.03.2014
comment
или мы можем указать контроллер в теге заголовка, нет необходимости в глобальном контроллере в заголовке html: ‹title ng-controller = titleCtrl› {{Page.title ()}} ‹/title› - person Dmitri Algazin; 14.05.2014
comment
Я должен признать, хотя я проголосовал за этот ответ, использование атрибута ng-bind-template={{pageTitle}} в элементе title намного чище. Это предотвращает отображение заголовка браузера {{pageTitle}} до тех пор, пока приложение не загрузится. - person ; 23.05.2014
comment
Спасибо, ваш пример заставил меня понять, что я не использую сервисы в полной мере. - person RachelD; 02.06.2014
comment
Page.title () у меня не работал, вместо этого я просто использовал $ scope.pageTitle = Page.title (); и использовал {{pageTitle}} в шаблоне. Работает и спасибо! - person CIF; 03.07.2014
comment
Лично я предпочитаю установить заголовок на $rootScope вместо того, чтобы создавать дополнительный контроллер. - person DDA; 19.07.2014
comment
Как вы устанавливаете $ locationProvider, когда я вставляю ваш точный код, он выдает ошибку - person Leon Gaban; 11.10.2014
comment
Я реализовал это решение для заголовка и описания, и оно отлично работает, за исключением случаев, когда я проверяю свою страницу на woorank, оно показывает, что заголовок отсутствует, а описание - {{Page.getDescription ()}}. Значит ли это, что поисковые системы тоже получат то же самое ??? - person Graham Fowles; 12.11.2014
comment
Я лично считаю, что решение г-на Хэша (здесь по тому же вопросу) намного лучше: stackoverflow.com/a/17898250/448816 Это позволяет динамически генерировать заголовок страницы на основе маршрута и при необходимости вставлять настраиваемый заголовок из контроллера. - person mikhail-t; 11.02.2015
comment
Заголовок устанавливает только «по умолчанию». Почему? - person Faradox; 07.08.2015

Я только что обнаружил хороший способ установить заголовок вашей страницы, если вы используете маршрутизацию:

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 &mdash; ' + title">myApp</title>
...

Изменить: используйте атрибут ng-bind вместо фигурных скобок {{}}, чтобы они не отображались при загрузке

person jkoreska    schedule 15.11.2012
comment
Это не позволяет динамически настраивать заголовок на основе переменных $ scope. - person Eric Drechsel; 16.05.2013
comment
@EricDrechsel Вы можете установить $ rootScope.title из других мест в вашем приложении, эта логика изменяет его только в $ routeChangeSuccess. - person jkoreska; 16.05.2013
comment
верно, но в вашем примере не показано, как изменить заголовок в $ routeChangeSuccess, параметризованном переменными $ scope, что делает пример @tosh с использованием службы страницы. Таким образом, вы можете установить title = "Blog", но не title = '{{"Blog post " + post.title}}'. - person Eric Drechsel; 21.05.2013
comment
На самом деле это двойной $ в имени свойства: current. $$ route.title, возможно, stackoverflow поглотил другой $ - person Chris Sattinger; 05.06.2013
comment
@felix, вы также можете получить доступ к заголовку, например current.title - person Eldelshell; 18.07.2013
comment
Или используйте директиву ng-bind-template и синтаксис фигурных скобок. Судя по docs, этот атрибут был добавлен для установки заголовка документа. . - person Shrikant Sharat; 08.09.2013
comment
$ rootScope.title = текущий. $ route.title; без двоих $$ - person david.sansay; 12.09.2013
comment
Я только что обновил свою версию Angular на несколько версий (с 1.0.5 до 1.2.7), и это сломало меня в моем коде. Я использовал current.$route в старом коде, и он работал. При обновлении требуется двойной $ на маршруте. current.$$route - person Tyler Forsythe; 19.03.2014
comment
Это здорово, НО единственное, что $$ маршрут считается частным. - person Chad Johnson; 23.05.2014
comment
фигурные скобки не отображаются, если вы используете ng-cloak - person blockloop; 18.07.2014
comment
@EricDrechsel использует параметр resolve при попытке использовать динамические переменные. resolve: { blog: function($route, Blog) { $route.current.$$route.title = /*etc*/ } } - person johngeorgewright; 28.11.2014
comment
У кого-нибудь есть проблемы с тем, чтобы это работало в Angular 1.3.4? Событие запускается, значения верны, но привязка никогда не обновляет текст. В моем случае у меня есть привязка к тегу span. - person David Anderson; 02.12.2014
comment
В ответчике когда видно '/Product/:id'. Есть ли способ получить значение :id с помощью этого метода? Я пробовал title: function(params){return params.id;}, но не получается ... Может, использую resolve? - person jbltx; 08.12.2014
comment
Зависимость $location не используется / не требуется. (Проверено, не нужно) - person Richard de Wit; 19.02.2015
comment
Мне нравится использовать этот метод, но я добавляю резервный вариант в ng-bind. что-то вроде `ng-bind = title || 'Мой крутой сайт' ' - person James Harrington; 02.04.2015
comment
Это решение отлично работает ... пока вы не используете .otherwise({redirectTo: '/'});. В этом случае заголовок пуст, а значение заголовка из маршрута '/' не используется. Кто-нибудь знает решение для этого случая? - person Tomino; 14.04.2015
comment
@Tomino Просто сделайте это в своем .run-коде $rootScope.title = current.$$route !== undefined ? current.$$route.title : 'your home page title here'; Это обеспечит наличие маршрута $$ перед его применением. Устраняет проблему! - person Mark Pieszak - Trilon.io; 05.05.2015
comment
Ваше решение решило мою проблему с исчезновением заголовка при перезагрузке страницы (F5). Отличный ответ! - person Taiwei Tuan; 29.08.2015
comment
Это лучший способ сделать это. Если вашему приложению требуется какое-то время для загрузки, другое решение покажет предварительно проанализированный {{var}} на вкладке перед запуском angular. Поэтому использование ng-bind - лучшее решение. Отлично сделано. - person Alex Boisselle; 18.02.2016
comment
Что мне подходит: ‹title ng-bind = 'Мое приложение | '+ LANG [' MENU_ '+ $ state.current.name.replace (' панель управления. ',' ') .ToUpperCase ()] ›Мое приложение ‹/title› - person Alex Boisselle; 18.02.2016
comment
Хорошие решения .... Будет ли это проиндексировано в google ... as Fetch as google показывает ‹title ng-bind = 'myApp' + title› myApp ‹/title› в своем загруженном HTTP-ответе: ... - person Asad Ali Khan; 18.06.2016
comment
Установить заголовок из контроллера (верхний уровень) можно с помощью $ scope. $ Parent.title = 'My Dynamic Title'; (если вы используете угловую маршрутизацию) - person Warren; 30.09.2016
comment
если вы используете stateProvider, вы можете сделать это $ rootScope. $ on ('$ stateChangeSuccess', function (ev, to, toParams, from, fromParams) {$ rootScope.title = to.title;}); - person Cengkuru Michael; 07.02.2017
comment
Я бы так и сделал, если бы не языковой перевод в моем веб-приложении - person Jens Alenius; 26.04.2017

Обратите внимание, что вы также можете установить заголовок напрямую с помощью javascript, т.е.

$window.document.title = someTitleYouCreated;

У этого нет привязки к данным, но этого достаточно, когда вставка ng-app в тег <html> проблематична. (Например, при использовании шаблонов JSP, где <head> определен ровно в одном месте, но у вас есть несколько приложений.)

person broc.seib    schedule 28.01.2013
comment
Хороший прямой подход, который напоминает вам об очевидном. Я просто обернул это в свою службу ui как функцию setTitle (), ввел зависимость $ window, и, поскольку моя служба находится во всех моих контроллерах, теперь могу вызывать ui.setTitle ('example title') везде, где мне нужно, в любом контроллер. - person Lukus; 31.07.2013
comment
Для меня это был единственный способ заставить его работать в Internet Explorer, хотя другие методы работали и в других браузерах. - person Maarten; 05.08.2013
comment
Как упоминал Мартен, это единственный подход, который работает в ie7 и ie8. - person rob; 18.09.2013
comment
Удивительно, как люди не могут сделать шаг назад и увидеть, как легко сделать это без прицелов и фабрик. - person redben; 27.03.2014
comment
Невероятный. Это было намного проще, чем все махинации, о которых упоминали другие. Спасибо! - person Leonard Teo; 01.05.2014
comment
Я смог успешно сделать это с контроллера после получения данных из вызова веб-API, и это был единственный метод, который сработал для меня, но он работал только тогда, когда я не начинал строку со знака доллара. Мне пришлось использовать window.document.title, а не $ window.document.title. - person StackOverflowUser; 22.12.2014
comment
Можно использовать обычное «окно» - оно действует непосредственно на DOM. $ window - это угловатая вещь, и вам нужно ввести ее, чтобы использовать. В любом случае будет работать. - person broc.seib; 22.12.2014
comment
$ window - это всего лишь оболочка, поэтому вы можете интегрировать ее и манипулировать ею для целей автоматического тестирования. Если вы просто хотите что-то установить, сделайте это напрямую :) - person sidonaldson; 08.04.2015
comment
$ document [0] .title также должен работать. Использование angular оберток документов и окон хорошо для последнего тестирования, предотвращения предупреждений jshint и т. Д. - person sgimeno; 19.10.2015
comment
однозначно лучший ответ! такой легкий и прямой. - person Rémi; 04.04.2016
comment
Мне пришлось установить заголовок внутри моего контроллера с именем категории. Я просто установил заголовок после того, как сделал значение, одну строку кода. - person MrBoJangles; 16.12.2016
comment
У этого нет привязки, ... Мне было бы любопытно, в каком сценарии привязка заголовка должна быть двусторонней. Я уверен, что сценарий есть, просто не знаю, каким он будет. Возможно конфетка 3 ... - person MrBoJangles; 16.12.2016
comment
Я имею в виду одностороннюю привязку, т.е. присвоение значения переменной, которая волшебным образом меняет заголовок окна. - person broc.seib; 16.12.2016
comment
Можно ли (но, что важнее всего, безопасно) сделать это с помощью ngStorage? - person LatentDenis; 26.04.2017
comment
повлияет ли это на SEO? - person Etherealm; 17.08.2017
comment
Самый удобный способ для существующих приложений. - person sashaegorov; 10.11.2017
comment
Именно то, что я искал! Спасибо - person CBB; 16.11.2019

Объявление ng-app в элементе html обеспечивает корневую область для head и body.

Поэтому в вашем контроллере введите $rootScope и установите для него свойство заголовка:

function Test1Ctrl($rootScope, $scope, $http) { $rootScope.header = "Test 1"; }

function Test2Ctrl($rootScope, $scope, $http) { $rootScope.header = "Test 2"; }

и на вашей странице:

<title ng-bind="header"></title>
person Andy Hitchman    schedule 31.05.2013
comment
лучший ответ на мой взгляд. В этом случае бесполезно иметь контроллер на уровне ng-app, как описано в принятом ответе. - person Nicolas Janel; 01.06.2014
comment
Мне нравится, насколько это легкое решение, и в нем не используются свойства $$. - person tedwards947; 04.09.2014
comment
Принятый ответ добавляет ненужных сложностей и рисков. В этой версии это так же просто, как установка переменной. - person special0ne; 11.11.2014
comment
Этот ответ сработал лучше всего. Самое элегантное решение. - person Natus Drew; 15.11.2014
comment
Если вы твердо настроены на использование $ rootScope, я бы по крайней мере извлек его в службу, чтобы у вас не было $ rootScope в вашем контроллере. - person Michael J. Calkins; 05.12.2014
comment
Я хочу использовать это решение, но мне любопытно, каковы преимущества его использования перед document.title = "App"; - person remarsh; 11.12.2014
comment
Ваше решение работает в этом сценарии? stackoverflow.com/questions/28760904/ - person Rober; 27.02.2015
comment
Достаточно хорошо для небольшого приложения с небольшим количеством частичных просмотров. - person TechCrunch; 03.03.2015
comment
ответ, который я в итоге использовал; хотя я использую одну фабрику, чтобы сопоставить все URL-адреса и установить заголовок / описание, чтобы не требовать $ rootScope во всех контроллерах - person Eolis; 05.02.2016
comment
один из самых простых способов - person Saurabh Solanki; 20.09.2018

Модуль angularjs-viewhead показывает механизм для установки заголовка для каждого просмотра с использованием только настраиваемая директива.

Его можно применить к существующему элементу представления, содержимое которого уже является заголовком представления:

<h2 view-title>About This Site</h2>

... или его можно использовать как отдельный элемент, и в этом случае элемент будет невидимым в визуализированном документе и будет использоваться только для установки заголовка представления:

<view-title>About This Site</view-title>

Содержимое этой директивы доступно в корневой области как viewTitle, поэтому его можно использовать в элементе заголовка, как и любую другую переменную:

<title ng-bind-template="{{viewTitle}} - My Site">My Site</title>

Его также можно использовать в любом другом месте, которое может «видеть» корневую осциллографию. Например:

<h1>{{viewTitle}}</h1>

Это решение позволяет устанавливать заголовок с помощью того же механизма, который используется для управления остальной частью презентации: шаблонов AngularJS. Это избавляет от необходимости загромождать контроллеры такой логикой представления. Контроллер должен сделать доступными любые данные, которые будут использоваться для информирования заголовка, но окончательное решение о том, как его представить, принимает шаблон и может использовать интерполяцию выражений и фильтры для привязки к данным области как обычный.

(Отказ от ответственности: я являюсь автором этого модуля, но я ссылаюсь на него здесь только в надежде, что он поможет кому-то другому решить эту проблему.)

person Martin Atkins    schedule 08.12.2013
comment
Не могу поверить, что это решение не получило больше голосов. Большинство других - действительно плохой выбор дизайна. - person Martin Wawrusch; 17.08.2014
comment
Согласен, это должно быть лучшим решением. Мне это нравится намного больше, чем объявление контроллера на уровне страницы для установки заголовка. К вашему сведению: используя это с Angular v1.3.2 и angular-route-segment v1.3.3, и он работает как шарм. - person Nate Barbettini; 12.12.2014
comment
Я поддерживаю это решение;) - person jkoreska; 10.04.2015
comment
Я написал немного больше об angularjs-viewhead и другой связанной идее здесь, в моем блоге: очевидно .me.uk / angularjs-view-specific-sidebars. - person Martin Atkins; 14.06.2015
comment
При повторном использовании одного и того же представления на верхнем уровне и на представлении подуровня можно по-прежнему использовать заголовок представления с ng-if, например: ‹h4 ng-if = $ state.includes ('some-state') view- title ›Подробная информация о {{...}} ‹/h4›‹ h4 ng-if =! $ state.includes ('some-state') ›Подробнее о {{...}} ‹/h4› - person anre; 29.01.2016
comment
Это было самое простое решение, лучшее, с моей скромной точки зрения. - person Abiel Muren; 14.03.2017
comment
Такой чистый и элегантный! Единственное решение, в котором у вас могут быть тысячи разных заголовков, которые будут автоматически синхронизироваться при смене языка, пользователей и т. Д., Не беспокоясь об этом. - person Robycool; 08.05.2017

Вот адаптированное решение, которое работает для меня, которое не требует инъекции $ rootScope в контроллеры для установки заголовков страниц для конкретных ресурсов.

В главном шаблоне:

<html data-ng-app="myApp">
    <head>
    <title data-ng-bind="page.title"></title>
    ...

В конфиге маршрутизации:

$routeProvider.when('/products', {
    title: 'Products',
    templateUrl: '/partials/products.list.html',
    controller: 'ProductsController'
});

$routeProvider.when('/products/:id', {
    templateUrl: '/partials/products.detail.html',
    controller: 'ProductController'
});

И в блоке запуска:

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

Наконец, в контроллере:

function ProductController($scope) {
    //Load product or use resolve in routing
    $scope.page.setTitle($scope.product.name);
}
person Mr Hash    schedule 27.07.2013
comment
Заголовок, установленный в ProductController ($ scope.page.setTitle), переопределяется $ rootScope. $ On ('$ routeChangeSuccess'. Установка заголовка по умолчанию в $ rootScope. $ On ('$ routeChangeStart' в этом отношении безопаснее. - person Kristo Aun; 08.02.2015
comment
@ mr-hash: вот небольшая корректировка, которую я предлагаю, идеально подходит для существующих проектов angular со многими маршрутами, но без заголовков. Он сгенерирует заголовок из имени контроллера, если заголовок не определен на маршруте: $rootScope.page.setTitle(current.$$route.title || current.$$route.controller.replace('Ctrl', '')); - person mikhail-t; 11.02.2015
comment
не забудьте очистить вывод следующим образом: this.title = title.replace('<', '&lt;').replace('>', '&gt;').replace(' & ', ' &amp; ') + ' | Site Name'; - person Henrik Stenbæk; 06.08.2015
comment
Я получил неопределенную ошибку, поэтому я изменил последний бит на: $ rootScope.page.title = current. $$ route? текущий. $$ route.title + '| Имя сайта ':' Имя сайта '; - person Andy; 31.01.2016

Решение jkoreska идеально подходит, если вы знаете заголовки заранее, но вам может потребоваться установить заголовок на основе данных, которые вы получаете из ресурса и т. д.

Мое решение требует единой службы. Поскольку rootScope является основой всех элементов DOM, нам не нужно помещать контроллер в элемент html, как кто-то упомянул

Page.js

app.service('Page', function($rootScope){
    return {
        setTitle: function(title){
            $rootScope.title = title;
        }
    }
});

index.jade

doctype html
html(ng-app='app')
head
    title(ng-bind='title')
// ...

Все контроллеры, которым нужно изменить заголовок

app.controller('SomeController', function(Page){
    Page.setTitle("Some Title");
});
person Deminetix    schedule 17.03.2014
comment
небольшая проблема, когда вы обновляете страницу, в названии вкладки вы видите '{{title}}', а после отображения страницы вы видите только 'Some Title'. решение с фабрикой не имеет такого поведения - person Dmitri Algazin; 14.05.2014
comment
вместо {{title}} используйте ng-bind='title' - person Faradox; 07.08.2015
comment
Согласитесь с @Faradox ... использование ng-bind предотвращает отображение предварительно интерполированного синтаксиса до того, как заголовок фактически оценивается. +100 - person Seth; 24.09.2015

Чистый способ, позволяющий динамически устанавливать заголовок или мета-описание. В примере я использую ui-router, но вы можете использовать ngRoute таким же образом.

var myApp = angular.module('myApp', ['ui.router'])

myApp.config(
    ['$stateProvider', function($stateProvider) {
        $stateProvider.state('product', {
            url: '/product/{id}',
            templateUrl: 'views/product.html',
            resolve: {
                meta: ['$rootScope', '$stateParams', function ($rootScope, $stateParams) {
                    var title = "Product " + $stateParams.id,
                        description = "Product " + $stateParams.id;
                    $rootScope.meta = {title: title, description: description};
                }]

                // Or using server side title and description
                meta: ['$rootScope', '$stateParams', '$http', function ($rootScope, $stateParams, $http) {
                    return $http({method: 'GET', url: 'api/product/ + $stateParams.id'})
                        .then (function (product) {
                            $rootScope.meta = {title: product.title, description: product.description};
                        });
                }]

            }
            controller: 'ProductController'
        });
    }]);

HTML:

<!DOCTYPE html>
<html ng-app="myApp">
<head>
    <title ng-bind="meta.title + ' | My App'">myApp</title>
...
person Alex Soroka    schedule 06.07.2014

В качестве альтернативы, если вы используете ui-router:

index.html

<!DOCTYPE html>
<html ng-app="myApp">
<head>
    <title ng-bind="$state.current.data.title || 'App'">App</title>

Маршрутизация

$stateProvider
  .state('home', {
      url: '/',
      templateUrl: 'views/home.html',
      data: {
        title: 'Welcome Home.'
      }
  }
person Nathan Kot    schedule 25.07.2014
comment
Я не могу заставить это работать ... У меня ui-router обновленный URL-адрес и контент в зависимости от моего состояния, и я не получаю ошибок или предупреждений, но я не могу получить доступ к какой-либо части объекта конфигурации состояния через $state.current.[...]. Какую версию ui-router вы использовали для этого? - person ; 08.11.2014
comment
Редактирование My Runtime Config для ответа решает проблему, о которой я упоминал в моем комментарии выше. :) Я открыт для идей, если есть лучший способ сделать это. - person ; 08.11.2014
comment
это не работает для меня, и «title» не находится в документации API - это все еще поддерживается? - person GraehamF; 14.01.2016

Индивидуальное решение на основе событий

Вот еще один подход, который не упоминался другими здесь (на момент написания этой статьи).

Вы можете использовать собственные события следующим образом:

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

Этот подход имеет то преимущество, что не требует написания дополнительных сервисов и их последующего внедрения в каждый контроллер, которому необходимо установить заголовок, а также (ab) не использует $rootScope. Он также позволяет вам установить динамический заголовок (как в примере кода), что невозможно с использованием настраиваемых атрибутов данных в объекте конфигурации маршрутизатора (по крайней мере, насколько мне известно).

person Michael Bromley    schedule 27.03.2014

Для сценариев, в которых у вас нет ngApp, содержащего тег title, просто вставьте службу в контроллеры, которым необходимо установить заголовок окна.

var app = angular.module('MyApp', []);

app.controller('MyController', function($scope, SomeService, Title){
    var serviceData = SomeService.get();
    Title.set("Title of the page about " + serviceData.firstname);
});

app.factory('SomeService', function ($window) {
    return {
        get: function(){
            return { firstname : "Joe" };
        }
    };
});

app.factory('Title', function ($window) {
    return {
        set: function(val){
            $window.document.title = val;
        }
    };
});

Рабочий пример ... http://jsfiddle.net/8m379/1/

person JeremyWeir    schedule 19.07.2013

Если у вас нет контроля над элементом заголовка (например, веб-формой asp.net), вот что вы можете использовать

var app = angular.module("myApp")
    .config(function ($routeProvider) {
                $routeProvider.when('/', {
                                            title: 'My Page Title',
                                            controller: 'MyController',
                                            templateUrl: 'view/myView.html'
                                        })
                            .otherwise({ redirectTo: '/' });
    })
    .run(function ($rootScope) {
        $rootScope.$on("$routeChangeSuccess", function (event, currentRoute, previousRoute) {
            document.title = currentRoute.title;
        });
    });
person Ashish    schedule 06.08.2013

Простой и грязный способ с использованием $rootScope:

<html ng-app="project">
<head>
<title ng-bind="title">Placeholder title</title>

В ваших контроллерах, когда у вас есть данные, необходимые для создания заголовка, выполните:

$rootScope.title = 'Page X'
person user1338062    schedule 18.11.2013

Ни один из этих ответов не казался достаточно интуитивным, поэтому я создал небольшую директиву для этого. Этот способ позволяет вам объявить заголовок на странице там, где это обычно делается, а также позволяет сделать его динамическим.

angular.module('myModule').directive('pageTitle', function() {
    return {
        restrict: 'EA',
        link: function($scope, $element) {
            var el = $element[0];
            el.hidden = true; // So the text not actually visible on the page

            var text = function() {
                return el.innerHTML;
            };
            var setTitle = function(title) {
                document.title = title;
            };
            $scope.$watch(text, setTitle);
        }
    };
});

Конечно, вам нужно будет изменить имя модуля, чтобы оно соответствовало вашему.

Чтобы использовать его, просто бросьте это в свое представление, как если бы вы это делали для обычного тега <title>:

<page-title>{{titleText}}</page-title>

Вы также можете просто включить простой текст, если он вам не нужен, динамически:

<page-title>Subpage X</page-title>

В качестве альтернативы вы можете использовать атрибут, чтобы сделать его более дружественным к IE:

<div page-title>Title: {{titleText}}</div>

Конечно, вы можете поместить в тег любой текст, в том числе код Angular. В этом примере он будет искать $scope.titleText в том контроллере, в котором сейчас находится тег custom-title.

Просто убедитесь, что на вашей странице нет нескольких тегов заголовков, иначе они будут сбивать друг друга.

Пример Plunker здесь http://plnkr.co/edit/nK63te7BSbCxLeZ2ADHV. Вам нужно будет загрузить zip-архив и запустить его локально, чтобы увидеть изменение заголовка.

person MikeyB    schedule 21.07.2014
comment
Я придумал нечто подобное. Безусловно, наиболее интуитивно понятен в использовании и не требует установки контроллера на html. В моей директиве я также добавляю необязательную константу pageTitlePrefix. - person z0r; 25.06.2015

Упрощенное решение для angular-ui-router:

HTML:

<html ng-app="myApp">
  <head>
     <title ng-bind="title"></title>
     .....
     .....  
  </head>
</html>

App.js> блок myApp.config

$stateProvider
    .state("home", {
        title: "My app title this will be binded in html title",
        url: "/home",
        templateUrl: "/home.html",
        controller: "homeCtrl"
    })

App.js> myApp.run

myApp.run(['$rootScope','$state', function($rootScope,$state) {
   $rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams) {
    $rootScope.title = $state.current.title;
    console.log($state);
   });
}]);
person the_mishra    schedule 09.06.2017

Вот другой способ изменить заголовок. Может быть, не такой масштабируемый, как фабричная функция (которая могла бы обрабатывать неограниченное количество страниц), но мне было легче понять:

В моем index.html я начал так:

    <!DOCTYPE html>
      <html ng-app="app">
        <head>
          <title ng-bind-template="{{title}}">Generic Title That You'll Never See</title>

Затем я сделал партиал под названием "nav.html":

<div ng-init="$root.title = 'Welcome'">
    <ul class="unstyled">
        <li><a href="#/login" ng-click="$root.title = 'Login'">Login</a></li>
        <li><a href="#/home" ng-click="$root.title = 'Home'">Home</a></li>
        <li><a href="#/admin" ng-click="$root.title = 'Admin'">Admin</a></li>
        <li><a href="#/critters" ng-click="$root.title = 'Crispy'">Critters</a></li>
    </ul>
</div>

Затем я вернулся к index.html и добавил nav.html, используя ng-include и ng-view для моих частичных файлов:

<body class="ng-cloak" ng-controller="MainCtrl">
    <div ng-include="'partials/nav.html'"></div>
    <div>
        <div ng-view></div>
    </div>

Обратите внимание на этот нг-плащ? Это не имеет ничего общего с этим ответом, но он скрывает страницу до тех пор, пока она не загрузится, приятное прикосновение :) Узнайте, как здесь: Angularjs - элементы ng-cloak / ng-show мигают

Вот базовый модуль. Я помещаю его в файл с названием "app.js":

(function () {
    'use strict';
    var app = angular.module("app", ["ngResource"]);

    app.config(function ($routeProvider) {
        // configure routes
        $routeProvider.when("/", {
            templateUrl: "partials/home.html",
            controller:"MainCtrl"
        })
            .when("/home", {
            templateUrl: "partials/home.html",
            controller:"MainCtrl"
        })
            .when("/login", {
            templateUrl:"partials/login.html",
            controller:"LoginCtrl"
        })
            .when("/admin", {
            templateUrl:"partials/admin.html",
            controller:"AdminCtrl"
        })
            .when("/critters", {
            templateUrl:"partials/critters.html",
            controller:"CritterCtrl"
        })
            .when("/critters/:id", {
            templateUrl:"partials/critter-detail.html",
            controller:"CritterDetailCtrl"
        })
            .otherwise({redirectTo:"/home"});
    });

}());

Если вы посмотрите в конец модуля, вы увидите, что у меня есть страница с подробностями о животных, основанная на: id. Это часть, которая используется на странице Crispy Critters. [Корни, я знаю - может быть, это сайт, посвященный всем видам куриных наггетсов;) В любом случае, вы можете обновить заголовок, когда пользователь нажимает на любую ссылку, поэтому на моей главной странице Crispy Critters, которая ведет на страницу с подробностями о животных, вот куда пойдет обновление $ root.title, как вы видели в nav.html выше:

<a href="#/critters/1" ng-click="$root.title = 'Critter 1'">Critter 1</a>
<a href="#/critters/2" ng-click="$root.title = 'Critter 2'">Critter 2</a>
<a href="#/critters/3" ng-click="$root.title = 'Critter 3'">Critter 3</a>

Извините, так ветрено, но я предпочитаю пост, который дает достаточно подробностей, чтобы запустить его. Обратите внимание, что страница примера в документации AngularJS устарела и показывает версию 0.9 ng-bind-template. Как видите, разница не так уж и велика.

Запоздалая мысль: вы это знаете, но это есть для всех; внизу index.html необходимо включить app.js с модулем:

        <!-- APP -->
        <script type="text/javascript" src="js/app.js"></script>
    </body>
</html>
person noogrub    schedule 21.09.2012
comment
Мое мнение, не используйте это. Вы смешиваете данные (информацию) в представлениях (презентации). Позже будет очень сложно найти источники заголовков, разбросанные по всем вашим HTML-ссылкам, которые могут присутствовать в разных местах в представлении. - person amit bakle; 22.07.2014
comment
Поскольку заголовок обновляется только после фактического нажатия на ссылку, это не устанавливает заголовок правильно, когда пользователь впервые попадает на страницу или когда пользователь обновляется. - person Mark Amery; 27.01.2015

Когда мне нужно было решить эту проблему, я не мог разместить ng-app в теге html страницы, поэтому я решил это с помощью службы:

angular.module('myapp.common').factory('pageInfo', function ($document) {

    // Public API
    return {
        // Set page <title> tag. Both parameters are optional.
        setTitle: function (title, hideTextLogo) {
            var defaultTitle = "My App - and my app's cool tagline";
            var newTitle = (title ? title : defaultTitle) + (hideTextLogo ? '' : ' - My App')
            $document[0].title = newTitle;
        }
    };

});
person Tom Söderlund    schedule 11.06.2015

Решение на основе настраиваемых событий, вдохновленное Майклом Бромли

Мне не удалось заставить его работать с $ scope, поэтому я попробовал с rootScope, может быть, немного грязнее ... (особенно если вы обновляете страницу, которая не регистрирует событие)

Но мне очень нравится идея о том, как все слабо связаны.

Я использую angularjs 1.6.9

index.run.js

angular
.module('myApp')
.run(runBlock);

function runBlock($rootScope, ...)
{
  $rootScope.$on('title-updated', function(event, newTitle) {
    $rootScope.pageTitle = 'MyApp | ' + newTitle;
  });
}

anyController.controller.js

angular
.module('myApp')
.controller('MainController', MainController);

function MainController($rootScope, ...)
{
  //simple way :
  $rootScope.$emit('title-updated', 'my new title');

  // with data from rest call
  TroncQueteurResource.get({id:tronc_queteur_id}).$promise.then(function(tronc_queteur){
  vm.current.tronc_queteur = tronc_queteur;

  $rootScope.$emit('title-updated', moment().format('YYYY-MM-DD') + ' - Tronc '+vm.current.tronc_queteur.id+' - ' +
                                             vm.current.tronc_queteur.point_quete.name + ' - '+
                                             vm.current.tronc_queteur.queteur.first_name +' '+vm.current.tronc_queteur.queteur.last_name
  );
 });

 ....}

index.html

<!doctype html>
<html ng-app="myApp">
  <head>
    <meta charset="utf-8">
    <title ng-bind="pageTitle">My App</title>

У меня работает :)


person Thomas    schedule 04.05.2018

В то время как у других могут быть лучшие методы, я смог использовать $ rootScope в своих контроллерах, поскольку каждый из моих представлений / шаблонов имеет отдельный контроллер. Вам нужно будет ввести $ rootScope в каждый контроллер. Хотя это может быть не идеально, он работает для меня, поэтому я подумал, что должен передать его. Если вы проверите страницу, она добавит привязку ng к тегу заголовка.

Пример контроллера:

myapp.controller('loginPage', ['$scope', '$rootScope', function ($scope, $rootScope) {

// Dynamic Page Title and Description
$rootScope.pageTitle = 'Login to Vote';
$rootScope.pageDescription = 'This page requires you to login';
}]);

Пример заголовка Index.html:

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="description" content="{{pageDescription}}">
<meta name="author" content="">
<link rel="shortcut icon" href="../../assets/ico/favicon.ico">
<base href="/">
<title>{{pageTitle}}</title>

Вы также можете установить для pageTitle и pageDescription динамические значения, такие как возврат данных из вызова REST:

    $scope.article = restCallSingleArticle.get({ articleID: $routeParams.articleID }, function() {
    // Dynamic Page Title and Description
    $rootScope.pageTitle = $scope.article.articletitle;
    $rootScope.pageDescription = $scope.article.articledescription;
});

Опять же, у других могут быть лучшие идеи о том, как подойти к этому, но поскольку я использую предварительный рендеринг, мои потребности удовлетворяются.

person Kode    schedule 08.03.2015

Спасибо tosh shimayama за его решение.
Я подумал, что было не так чисто помещать службу прямо в $scope, поэтому вот мой небольшой вариант: http://plnkr.co/edit/QJbuZZnZEDOBcYrJXWWs

Контроллер (который в исходном ответе мне показался слишком глупым) создает объект ActionBar, и этот объект помещается в $ scope.
Этот объект отвечает за фактический запрос службы. Он также скрывает от $ scope вызов для установки URL-адреса шаблона, который вместо этого доступен другим контроллерам для установки URL-адреса.

person superjos    schedule 06.03.2013

Mr Hash пока что дал лучший ответ, но приведенное ниже решение делает его идеальным (для меня), добавляя следующие преимущества:

  • Не добавляет часов, которые могут замедлить работу
  • Фактически автоматизирует то, что я мог бы сделать в контроллере, но
  • По-прежнему дает мне доступ с контроллера, если я все еще хочу.
  • Без дополнительных инъекций

В роутере:

  .when '/proposals',
    title: 'Proposals',
    templateUrl: 'proposals/index.html'
    controller: 'ProposalListCtrl'
    resolve:
      pageTitle: [ '$rootScope', '$route', ($rootScope, $route) ->
        $rootScope.page.setTitle($route.current.params.filter + ' ' + $route.current.title)
      ]

В блоке выполнения:

.run(['$rootScope', ($rootScope) ->
  $rootScope.page =
    prefix: ''
    body: ' | ' + 'Online Group Consensus Tool'
    brand: ' | ' + 'Spokenvote'
    setTitle: (prefix, body) ->
      @prefix = if prefix then ' ' + prefix.charAt(0).toUpperCase() + prefix.substring(1) else @prifix
      @body = if body then ' | ' + body.charAt(0).toUpperCase() + body.substring(1) else @body
      @title = @prefix + @body + @brand
])
person Kim Miller    schedule 23.08.2014

Лучшее и динамическое решение, которое я нашел, - это использовать $ watch для отслеживания изменений переменных, а затем обновить заголовок.

person geolyth    schedule 31.12.2014