如何动态更改基于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"; } 

您可以在<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

我刚刚发现了一个很好的方法来设置你的页面标题,如果你使用路由:

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属性而不是curlies {{}}以便在加载时不显示

请注意,您也可以直接用javascript设置标题,

 $window.document.title = someTitleYouCreated; 

这没有数据绑定,但只要在<html>标记中放置ng-app有问题就足够了。 (例如,使用JSP模板,其中<head>仅在一个地方定义,但您有多个应用程序。)

html元素上声明ng-appheadbody提供了根范围。

因此,在你的控制器中注入$rootScope并设置一个标题属性:

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

并在你的页面:

 <title ng-bind="header"></title> 

模块angularjs-viewhead显示了一种机制,只使用自定义指令来设置每个视图的标题。

它可以应用于其内容已经是视图标题的现有视图元素:

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

…或者它可以作为一个独立的元素使用,在这种情况下,元素在渲染文档中是不可见的,只能用来设置视图标题:

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

这个指令的内容作为viewTitle在根作用域中viewTitle ,所以它可以像其他任何变量一样在标题元素上使用:

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

它也可以用在任何其他可以“看到”根作用域的地方。 例如:

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

该解决方案允许通过用于控制演示文稿的其余部分的相同机制来设置标题:AngularJS模板。 这避免了使用这种表示逻辑来混淆控制器的需要。 控制器需要提供任何将用于通知标题的数据,但是模板会对如何呈现标题做出最终决定,并且可以使用表达式插值和过滤器来正常绑定范围数据。

(免责声明:我是这个模块的作者,但是我在这里引用它只是希望它能帮助别人解决这个问题。)

这里有一个适合我的方案,它不需要将$ 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); } 

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

一个干净的方式,允许动态设置标题或元描述。 在例子中我使用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> ... 

另外,如果你使用的是路由器 :

的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.' } } 

如果你没有控制标题元素(如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; }); }); 

基于事件的自定义解决方案

这里是另外一种方法,这里没有提到其他人(截至撰写本文)。

你可以使用像这样的自定义事件:

 // 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 。 它还允许你设置一个动态标题(如代码示例中所示),这是使用路由器配置对象上的自定义数据属性所不可能的(据我所知至少)。

这些答案都不够直观,所以我创建了一个小指令来做到这一点。 通过这种方式,您可以在页面中声明标题,通常在该页面中执行此操作,并允许它也是动态的。

 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代码。 在这个例子中,它将在custom-title标签当前​​所在的控制器中寻找$scope.titleText

只要确保你的页面上没有多个页面标题标签,否则他们会互相扯皮。

Plunker示例在这里http://plnkr.co/edit/nK63te7BSbCxLeZ2ADHV 。 您必须下载zip并在本地运行,才能看到标题更改。

这是一个不同的方式来做标题更改。 也许不像工厂函数那样可扩展(可以设想处理不受限制的页面),但是对我来说更容易理解:

在我的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”,并使用ng-include和ng-view为我的分支添加了nav.html:

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

注意ng-cloak? 这与这个答案没有任何关系,但它隐藏了页面,直到完成加载,一个很好的触摸:)了解如何在这里: 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页面使用的部分内容。 [Corny,我知道 – 也许这是一个庆祝各种鸡块的网站;无论如何,当用户点击任何链接时,你可以更新标题,所以在我的主要Crispy Critter页面导致生物细节页面,这就是$ 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文档中的示例页面已过期,并显示了ng-bind-template的0.9版本。 你可以看到它没有太大的不同。

事后思考:你知道这一点,但这是为了别人。 在index.html的底部,必须包含app.js和模块:

  <!-- APP --> <script type="text/javascript" src="js/app.js"></script> </body> </html> 

对于没有包含title标签的ngApp的情况,只需将服务注入需要设置窗口标题的控制器。

 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/

简单和肮脏的方式使用$rootScope

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

在您的控制器中,当您有创建标题所需的数据时,请执行:

 $rootScope.title = 'Page X' 

当我必须解决这个问题的时候,我不能把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; } }; }); 

而其他人可能有更好的方法,我可以在我的控制器中使用$ rootScope,因为我的每个视图/模板都有一个独特的控制器。 您需要在每个控制器中注入$ rootScope。 虽然这可能不是理想的,但它对我来说是功能性的,所以我认为我应该通过它。 如果你检查页面,它将ng-binding添加到标题标签。

示例控制器:

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

再次,其他人可能有更好的想法如何处理这个问题,但由于我使用预渲染,我的需求正在满足。

感谢他的解决方案, 托什shimayama 。
我认为把服务直接放在$scope中并不是那么干净,所以这里是我的细微变化: http : //plnkr.co/edit/QJbuZZnZEDOBcYrJXWWs

控制器(在原来的答案在我看来有点太愚蠢)创建一个ActionBar对象,这一个被塞进$ scope。
该对象负责实际查询服务。 它也从$ scope范围隐藏设置模板URL的调用,而其他控制器可以使用该模板来设置URL。

到目前为止, 哈希先生的答案是最好的,但下面的解决方案通过增加以下好处使其理想(对我来说):

  • 不添加手表,这可能会减慢速度
  • 实际上,我自己可能已经在控制器中做了什么
  • 如果我仍然想要的话,仍然可以让我从控制器访问。
  • 没有额外的注射

在路由器中:

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

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

我发现更好的和动态的解决方案是使用$ watch跟踪变量更改,然后更新标题。