我正在使用 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"; }
如果您使用路由,我刚刚发现了一种设置页面标题的好方法:
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 — ' + title">myApp</title>
...
编辑:使用 ng-bind
属性而不是 curlies {{}}
,这样它们就不会在加载时显示
您可以在 <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
$rootScope
上设置标题,而不是创建额外的控制器。
注意,你也可以直接用javascript设置标题,即,
$window.document.title = someTitleYouCreated;
这没有数据绑定,但在将 ng-app
放入 <html>
标记有问题时就足够了。 (例如,使用 JSP 模板,其中 <head>
仅在一处定义,但您拥有多个应用程序。)
在 html
元素上声明 ng-app
可为 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>
document.title = "App"
;
模块 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 模板。这避免了使用这种表示逻辑使控制器混乱的需要。控制器需要提供将用于通知标题的任何数据,但模板会最终决定如何呈现它,并且可以使用表达式插值和过滤器来正常绑定到范围数据。
(免责声明:我是这个模块的作者,但我在这里引用它只是希望它能帮助其他人解决这个问题。)
这是一个适合我的解决方案,它不需要将 $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);
}
$rootScope.page.setTitle(current.$$route.title || current.$$route.controller.replace('Ctrl', ''));
this.title = title.replace('<', '<').replace('>', '>').replace(' & ', ' & ') + ' | Site Name';
如果您事先知道标题,jkoreska 的解决方案是完美的,但您可能需要根据从资源等中获得的数据设置标题。
我的解决方案需要一项服务。由于 rootScope 是所有 DOM 元素的基础,我们不需要像有人提到的那样在 html 元素上放置控制器
页面.js
app.service('Page', function($rootScope){
return {
setTitle: function(title){
$rootScope.title = title;
}
}
});
索引.jade
doctype html
html(ng-app='app')
head
title(ng-bind='title')
// ...
所有需要更改标题的控制器
app.controller('SomeController', function(Page){
Page.setTitle("Some Title");
});
{{title}}
使用 ng-bind='title'
ng-bind
防止在标题实际计算之前显示预插值语法。 +100
一种允许动态设置标题或元描述的干净方式。在示例中,我使用 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>
...
或者,如果您使用 ui-router:
索引.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.'
}
}
ui-router
根据我的状态更新 URL 和内容,我没有收到任何错误或警告,但我似乎无法通过 { 访问状态配置对象的任何部分2}。您使用哪个版本的 ui-router
来执行此操作?
基于事件的定制解决方案
这是此处其他人未提及的另一种方法(在撰写本文时)。
您可以像这样使用自定义事件:
// 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
。它还允许您设置动态标题(如代码示例中所示),这是无法使用路由器配置对象上的自定义数据属性(至少据我所知)。
对于没有包含 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/
如果您无法控制标题元素(如 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;
});
});
使用 $rootScope
的简单而肮脏的方式:
<html ng-app="project">
<head>
<title ng-bind="title">Placeholder title</title>
在您的控制器中,当您拥有创建标题所需的数据时,请执行以下操作:
$rootScope.title = 'Page X'
这些答案似乎都不够直观,所以我创建了一个小指令来做到这一点。这种方式允许您在页面中声明标题,通常会这样做,并允许它是动态的。
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
。
只要确保您的页面上没有多个页面标题标签,否则它们会互相破坏。
此处为 Plunker 示例 http://plnkr.co/edit/nK63te7BSbCxLeZ2ADHV。您必须下载 zip 并在本地运行它才能看到标题更改。
html
上放置控制器。在我的指令中,我还注入了一个可选的 pageTitlePrefix
常量。
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);
});
}]);
这是进行标题更改的另一种方法。也许不像工厂函数那样可扩展(可以想象它可以处理无限的页面),但我更容易理解:
在我的 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 添加了 nav.html,并为我的部分添加了 ng-view:
<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 elements blink
这是基本模块。我把它放在一个名为“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 的 critter-detail 页面。这是 Crispy Critters 页面中使用的部分内容。 [Corny,我知道 - 也许它是一个庆祝各种鸡块的网站;)无论如何,您可以在用户点击任何链接时更新标题,所以在我的 Crispy Critters 主页面中,该页面通向 critter-detail 页面,这就是 $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>
当我不得不解决这个问题时,我无法将 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;
}
};
});
受 Michael Bromley 启发的基于自定义事件的解决方案
我无法使其与 $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
);
});
....}
索引.html
<!doctype html>
<html ng-app="myApp">
<head>
<meta charset="utf-8">
<title ng-bind="pageTitle">My App</title>
它对我有用:)
虽然其他人可能有更好的方法,但我能够在我的控制器中使用 $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;
});
同样,其他人可能对如何解决这个问题有更好的想法,但由于我使用的是预渲染,所以我的需求得到了满足。
感谢 tosh shimayama 的解决方案。
我认为将服务直接放入 $scope
不太干净,所以我对此稍作改动:http://plnkr.co/edit/QJbuZZnZEDOBcYrJXWWs
控制器(最初的答案在我看来有点太笨了)创建了一个 ActionBar 对象,这个对象被塞进了 $scope。该对象负责实际查询服务。它还从 $scope 中隐藏了设置模板 URL 的调用,而其他控制器可以使用它来设置 URL。
到目前为止,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
])
我发现的更好的动态解决方案是使用 $watch 来跟踪变量更改,然后更新标题。
title = "Blog"
但不能设置title = '{{"Blog post " + post.title}}'
。current.title
这样的标题current.$route
并且它正在工作。随着升级,路线上需要双倍的美元。current.$$route
'/Product/:id'
。有没有办法用这种方法获得:id
值?我尝试了title: function(params){return params.id;}
但不起作用...也许使用resolve
?