How to dynamically change header based on AngularJS partial view?
我正在使用ng-view包含angularjs部分视图,我想基于包含的视图更新页面标题和h1标题标记。但是这些超出了部分视图控制器的范围,因此我无法确定如何将它们绑定到控制器中的数据集。
如果是ASP.NET MVC,您可以使用@viewbag来完成这项工作,但我不知道AngularJS中的等效项。我搜索过共享服务、事件等,但仍然无法使其正常工作。任何方法来修改我的例子,使其有效将是非常感谢。
我的HTML:
1 2 3 4 5 6 7 8 9 10 11 12 | <html data-ng-app="myModule"> <head> <!-- include js files --> <!-- should changed when ng-view changes --> </head> <body> <!-- should changed when ng-view changes --> </body> </html> |
我的JavaScript:
1 2 3 4 5 6 7 8 9 10 11 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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:
1 2 3 4 5 | <!DOCTYPE html> <html ng-app="myApp"> <head> <title ng-bind="'myApp — ' + title">myApp ... |
编辑:使用
您可以在
1 2 3 4 | <html ng-app="app" ng-controller="titleCtrl"> <head> {{ Page.title() }} ... |
创建服务:
1 2 3 4 5 6 7 | myModule.factory('Page', function() { var title = 'default'; return { title: function() { return title; }, setTitle: function(newTitle) { title = newTitle } }; }); |
注入
具体示例如下:http://plnkr.co/edit/0e7t6l
请注意,您也可以直接使用javascript设置标题,即,
1 | $window.document.title = someTitleYouCreated; |
这没有数据绑定,但当在
在
因此,在您的控制器中,注入
1 2 3 | function Test1Ctrl($rootScope, $scope, $http) { $rootScope.header ="Test 1"; } function Test2Ctrl($rootScope, $scope, $http) { $rootScope.header ="Test 2"; } |
在您的页面中:
1 | <title ng-bind="header"> |
模块angularJS viewhead显示了一种机制,可以仅使用自定义指令在每个视图的基础上设置标题。
它可以应用于内容已经是视图标题的现有视图元素:
1 | <h2 view-title>About This Site |
…或者它可以用作独立元素,在这种情况下,元素将在呈现的文档中不可见,并且仅用于设置视图标题:
1 | <view-title>About This Site</view-title> |
该指令的内容在根作用域中作为
1 | <title ng-bind-template="{{viewTitle}} - My Site">My Site |
它还可以用于任何其他可以"看到"根作用域的点。例如:
1 | {{viewTitle}} |
此解决方案允许通过用于控制其余演示文稿的相同机制来设置标题:angularJS模板。这就避免了使用这种表示逻辑来混乱控制器的需要。控制器需要提供任何用于通知标题的数据,但是模板最终决定如何显示,并且可以使用表达式插值和过滤器将其绑定到正常范围数据。
(免责声明:我是这个模块的作者,但我在这里引用它只是希望它能帮助其他人解决这个问题。)
这里有一个适合我的解决方案,它不需要向控制器中注入$rootscope来设置特定于资源的页面标题。
在主模板中:
1 2 3 4 | <html data-ng-app="myApp"> <head> <title data-ng-bind="page.title"> ... |
在路由配置中:
1 2 3 4 5 6 7 8 9 10 | $routeProvider.when('/products', { title: 'Products', templateUrl: '/partials/products.list.html', controller: 'ProductsController' }); $routeProvider.when('/products/:id', { templateUrl: '/partials/products.detail.html', controller: 'ProductController' }); |
在运行块中:
1 2 3 4 5 6 7 8 9 10 11 | 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'); }); }]); |
最后在控制器中:
1 2 3 4 | function ProductController($scope) { //Load product or use resolve in routing $scope.page.setTitle($scope.product.name); } |
Jkoreska的解决方案是完美的,如果你事先知道标题,但你可能需要根据从资源等获得的数据来设置标题。
我的解决方案需要单一服务。因为rootscope是所有dom元素的基础,所以我们不需要像前面提到的那样在html元素上放置控制器。
PJ.JS1 2 3 4 5 6 7 | app.service('Page', function($rootScope){ return { setTitle: function(title){ $rootScope.title = title; } } }); |
玉石索引
1 2 3 4 5 | doctype html html(ng-app='app') head title(ng-bind='title') // ... |
所有需要更改标题的控制器
1 2 3 | app.controller('SomeController', function(Page){ Page.setTitle("Some Title"); }); |
一种允许动态设置标题或元描述的干净方法。在示例中,我使用的是ui路由器,但您可以用同样的方式使用ngroute。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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:
1 2 3 4 5 | <!DOCTYPE html> <html ng-app="myApp"> <head> <title ng-bind="meta.title + ' | My App'">myApp ... |
或者,如果您使用的是UI路由器:
索引文件
1 2 3 4 | <!DOCTYPE html> <html ng-app="myApp"> <head> <title ng-bind="$state.current.data.title || 'App'">App |
路由
1 2 3 4 5 6 7 8 | $stateProvider .state('home', { url: '/', templateUrl: 'views/home.html', data: { title: 'Welcome Home.' } } |
自定义基于事件的解决方案
这里还有另一种方法,其他人没有提到(在本文中)。
您可以使用这样的自定义事件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // your index.html template <html ng-app="app"> <head> <title ng-bind="pageTitle">My App // 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)也不使用
如果您没有对标题元素(如ASP.NET Web窗体)的控制权,可以使用以下内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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; }); }); |
这些答案似乎都不够直观,所以我创建了一个小指令来做这件事。通过这种方式,您可以在页面中声明标题,通常可以在其中声明,并允许它也是动态的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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); } }; }); |
当然,您需要更改模块名以匹配您的模块名。
要使用它,只需将它放到您的视图中,就像在常规的
1 | <page-title>{{titleText}}</page-title> |
如果您不需要纯文本,也可以通过动态方式包含纯文本:
1 | <page-title>Subpage X</page-title> |
或者,可以使用属性,使其更友好:
1 | Title: {{titleText}} |
当然,您可以在标签中放入您想要的任何文本,包括角度代码。在本例中,它将在自定义标题标记当前所在的控制器中查找
只要确保你的页面上没有多个页面标题标签,否则它们会互相攻击。
Plnker示例:http://plnkr.co/edit/nk63te7bsbcxlez2adhv。您必须下载zip并在本地运行它,才能看到标题更改。
对于没有包含
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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/
使用
1 2 3 | <html ng-app="project"> <head> <title ng-bind="title">Placeholder title |
在控制器中,当您拥有创建标题所需的数据时,请执行以下操作:
1 | $rootScope.title = 'Page X' |
当我必须解决这个问题时,我不能将
1 2 3 4 5 6 7 8 9 10 11 12 13 | angular.module('myapp.common').factory('pageInfo', function ($document) { // Public API return { // Set page 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; } }; }); |
角度用户界面路由器的简化解决方案:
HTML:
1 2 3 4 5 6 7 | <html ng-app="myApp"> <head> <title ng-bind="title"> ..... ..... </head> </html> |
app.js>myapp.config块
1 2 3 4 5 6 7 | $stateProvider .state("home", { title:"My app title this will be binded in html title", url:"/home", templateUrl:"/home.html", controller:"homeCtrl" }) |
app.js>我的app.run
1 2 3 4 5 6 | 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中,我是这样开始的:
1 2 3 4 | <!DOCTYPE html> <html ng-app="app"> <head> <title ng-bind-template="{{title}}">Generic Title That You'll Never See |
然后我做了一个叫做"nav.html"的部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <ul class="unstyled"> <li> Login </li> <li> Home </li> <li> Admin </li> <li> Critters </li> </ul> |
然后我返回到"index.html"并使用ng include和ng视图为我的部分添加nav.html:
1 | <body class="ng-cloak" ng-controller="MainCtrl"> |
注意到那件NG斗篷了吗?它与这个答案没有任何关系,但是它会隐藏页面直到完成加载,一个很好的触摸:)在这里学习:angularjs-ng coat/ng s how elements blink
这是基本模块。我把它放在一个名为"app.js"的文件中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | (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的criter详细页面。这是crispy criters页面中使用的一个部分。[科尼,我知道-也许这是一个庆祝各种鸡块的网站;)不管怎样,当用户点击任何链接时,你都可以更新标题,所以在我的主crispy criters页面中,会出现criter detail页面,这就是$root.title更新的地方,就像你在上面的nav.html中看到的那样:
1 2 3 | Critter 1 Critter 2 Critter 3 |
对不起,这么多风,但我更喜欢一个能提供足够细节的帖子。注意,AngularJS文档中的示例页面已经过时,显示了0.9版本的ng绑定模板。你可以看到,这没什么不同。
事后想一想:你知道这一点,但它是为其他人准备的;在index.html的底部,你必须将app.js和模块包括在一起:
1 2 3 4 | <!-- APP --> <script type="text/javascript" src="js/app.js"> </body> </html> |
基于事件的定制解决方案灵感来自Michael Bromley
我不能让它与$scope一起工作,所以我尝试了rootscope,也许有点脏…(尤其是在不注册事件的页面上进行刷新时)
但我真的很喜欢事物松散耦合的概念。
我用的是AngularJS 1.6.9
索引文件
1 2 3 4 5 6 7 8 9 10 | angular .module('myApp') .run(runBlock); function runBlock($rootScope, ...) { $rootScope.$on('title-updated', function(event, newTitle) { $rootScope.pageTitle = 'MyApp | ' + newTitle; }); } |
任意控制器.controller.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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 ); }); ....} |
索引文件
1 2 3 4 5 | <!doctype html> <html ng-app="myApp"> <head> <meta charset="utf-8"> <title ng-bind="pageTitle">My App |
这对我有好处:)
虽然其他人可能有更好的方法,但我可以在控制器中使用$rootscope,因为我的每个视图/模板都有一个不同的控制器。您需要在每个控制器中注入$rootscope。虽然这可能不理想,但它对我来说是有用的,所以我想我应该把它传下去。如果检查页面,它会将ng绑定添加到标题标记中。
控制器示例:
1 2 3 4 5 6 | 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标题示例:
1 2 3 4 5 6 | <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="/"> {{pageTitle}} |
还可以将pageTitle和pageDescription设置为动态值,例如从REST调用返回数据:
1 2 3 4 5 | $scope.article = restCallSingleArticle.get({ articleID: $routeParams.articleID }, function() { // Dynamic Page Title and Description $rootScope.pageTitle = $scope.article.articletitle; $rootScope.pageDescription = $scope.article.articledescription; }); |
同样,其他人可能对如何处理这一点有更好的想法,但由于我使用的是预渲染,所以我的需求得到了满足。
到目前为止,hash先生得到了最好的答案,但下面的解决方案通过添加以下好处使其成为理想(对我而言):
- 不加手表,这样会减慢速度
- 实际上,我可以在控制器中实现自动化,但是
- 如果我还想要的话,仍然可以让我从控制器访问。
- 无额外注入
在路由器中:
1 2 3 4 5 6 7 8 | .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) ] |
在运行块中:
1 2 3 4 5 6 7 8 9 10 | .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 ]) |
多亏了石山的解决方案。我认为把一个服务直接放到
控制器(在最初的答案中我觉得有点笨)创建了一个actionbar对象,这个对象被填充到$scope中。对象负责实际查询服务。它还从$scope中隐藏了设置模板URL的调用,而其他控制器可以使用模板URL来设置该URL。
我找到的更好的动态解决方案是使用$watch跟踪变量更改,然后更新标题。