page change javascript angularjs templates partial-views angular-routing

javascript - change - ¿Cómo cambiar dinámicamente el encabezado según la vista parcial de AngularJS?



dynamic title angular 5 (22)

Solución basada en eventos personalizados inspirada en Michael Bromley

No pude hacerlo funcionar con $ scope, así que lo intenté con rootScope, quizás un poco más sucio ... (especialmente si haces una actualización en la página que no registra el evento)

Pero realmente me gusta la idea de cómo las cosas se acoplan libremente.

Estoy usando 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>

Esta trabajando para mi :)

Estoy usando ng-view para incluir vistas parciales de AngularJS, y quiero actualizar el título de la página y las etiquetas de encabezado h1 según la vista incluida. Sin embargo, estos están fuera del alcance de los controladores de vista parcial, por lo que no puedo averiguar cómo vincularlos al conjunto de datos en los controladores.

Si fuera ASP.NET MVC, podría usar @ViewBag para hacer esto, pero no sé el equivalente en AngularJS. He buscado sobre servicios compartidos, eventos, etc. pero todavía no puedo hacer que funcione. Cualquier forma de modificar mi ejemplo para que funcione sería muy apreciada.

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

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


Solución personalizada basada en eventos

Aquí hay otro enfoque que no ha sido mencionado por los otros aquí (a partir de este escrito).

Puedes usar eventos personalizados como:

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

Este enfoque tiene la ventaja de no requerir que se escriban servicios adicionales y luego se inyecten en cada controlador que necesita establecer el título, y tampoco (ab) utiliza el $rootScope . También le permite establecer un título dinámico (como en el ejemplo de código), que no es posible mediante el uso de atributos de datos personalizados en el objeto de configuración del enrutador (por lo menos que yo sepa).


Acabo de descubrir una buena forma de configurar el título de tu página si estás usando enrutamiento:

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

Edición : usar el atributo ng-bind lugar de los rizos {{}} para que no se muestren en la carga


Alternativamente, si está utilizando ui-router :

index.html

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

Enrutamiento

$stateProvider .state(''home'', { url: ''/'', templateUrl: ''views/home.html'', data: { title: ''Welcome Home.'' } }


Aquí hay una forma diferente de hacer cambios de título. Tal vez no tan escalable como una función de fábrica (que posiblemente podría manejar páginas ilimitadas) pero para mí era más fácil de entender:

En mi index.html empecé así:

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

Luego hice un parcial llamado "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>

Luego volví a "index.html" y agregué nav.html usando ng-include y ng-view para mis parciales:

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

Tenga en cuenta que ng-capa? No tiene nada que ver con esta respuesta, pero oculta la página hasta que termina de cargarse, un buen toque :) Aprende cómo: Angularjs - ng-cloak / ng-show elements blink

Aquí está el módulo básico. Lo puse en un archivo llamado "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"}); }); }());

Si miras hacia el final del módulo, verás que tengo una página de detalles de critter basada en: id. Es un parcial que se usa desde la página Crispy Critters. [Corny, lo sé, tal vez sea un sitio que celebra todo tipo de nuggets de pollo;) De todos modos, puedes actualizar el título cuando un usuario haga clic en cualquier enlace, así que en mi página principal de Crispy Critters que conduce a la página de detalles de critter, ahí es donde iría la actualización de $ root.title, tal como viste en el archivo nav.html anterior:

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

Lo siento mucho viento, pero prefiero una publicación que ofrezca suficientes detalles para ponerla en funcionamiento. Tenga en cuenta que la página de ejemplo en los documentos de AngularJS está desactualizada y muestra una versión 0.9 de ng-bind-template. Puedes ver que no es muy diferente.

Pensamiento posterior: lo sabes, pero está aquí para cualquier otra persona; en la parte inferior del index.html, uno debe incluir app.js con el módulo:

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


Aquí hay una solución adaptada que funciona para mí que no requiere la inyección de $ rootScope en los controladores para configurar títulos de página específicos de recursos.

En la plantilla maestra:

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

En la configuración de enrutamiento:

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

Y en el bloque de ejecución:

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

Finalmente en el controlador:

function ProductController($scope) { //Load product or use resolve in routing $scope.page.setTitle($scope.product.name); }


Cuando tuve que resolver esto, no pude colocar la ng-app en la etiqueta html la página, así que lo resolví con un servicio:

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


El módulo angularjs-viewhead muestra un mecanismo para configurar el título en base a cada vista utilizando solo una directiva personalizada.

Se puede aplicar a un elemento de vista existente cuyo contenido ya es el título de la vista:

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

... o se puede usar como un elemento independiente, en cuyo caso el elemento será invisible en el documento renderizado y solo se usará para establecer el título de la vista:

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

El contenido de esta directiva está disponible en el ámbito raíz como viewTitle , por lo que puede usarse en el elemento del título como cualquier otra variable:

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

También se puede utilizar en cualquier otro lugar que pueda "ver" el ámbito raíz. Por ejemplo:

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

Esta solución permite que el título se establezca a través del mismo mecanismo que se usa para controlar el resto de la presentación: las plantillas AngularJS. Esto evita la necesidad de saturar los controladores con esta lógica de presentación. El controlador debe poner a disposición cualquier dato que se utilizará para informar el título, pero la plantilla toma la decisión final sobre cómo presentarlo, y puede usar la interpolación de expresión y los filtros para enlazar a los datos de alcance de manera normal.

(Descargo de responsabilidad: Soy el autor de este módulo, pero lo estoy haciendo referencia aquí solo con la esperanza de que ayude a alguien más a resolver este problema).


Gracias a tosh shimayama por su solución.
Pensé que no era tan limpio poner un servicio directamente en el $scope , así que aquí está mi pequeña variación al respecto: http://plnkr.co/edit/QJbuZZnZEDOBcYrJXWWs

El controlador (que en la respuesta original me pareció un poco demasiado tonto) crea un objeto ActionBar, y este está metido en $ scope.
El objeto es responsable de consultar realmente el servicio. También oculta del alcance de $ la llamada para establecer la URL de la plantilla, que en su lugar está disponible para otros controladores para establecer la URL.


La declaración de ng-app en el elemento html proporciona un alcance raíz tanto para la head como para el body .

Por lo tanto, en su controlador inyecte $rootScope y establezca una propiedad de encabezado en esto:

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

y en tu pagina:

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


La solución de jkoreska es perfecta si conoce los títulos de antemano, pero es posible que deba establecer el título en función de los datos que obtenga de un recurso, etc.

Mi solución requiere un solo servicio. Ya que el rootScope es la base de todos los elementos DOM, no necesitamos poner un controlador en el elemento html como alguien mencionado

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'') // ...

Todos los controladores que necesitan cambiar el título.

app.controller(''SomeController'', function(Page){ Page.setTitle("Some Title"); });


La solución mejor y dinámica que he encontrado es usar $ watch para rastrear los cambios de la variable y luego actualizar el título.


Manera simple y sucia usando $rootScope :

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

En sus controladores, cuando tenga los datos necesarios para crear el título, haga:

$rootScope.title = ''Page X''


Mientras que otros pueden tener mejores métodos, pude usar $ rootScope en mis controladores, ya que cada una de mis vistas / plantillas tiene un controlador distinto. Deberá inyectar el $ rootScope en cada controlador. Si bien esto puede no ser ideal, está funcionando para mí, así que pensé que debería transmitirlo. Si inspecciona la página, agrega el enlace ng a la etiqueta del título.

Ejemplo de controlador:

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

Ejemplo de encabezado 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>

También puede configurar el título de la página y la descripción de la página a valores dinámicos, como devolver datos desde una llamada REST:

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

Nuevamente, otros pueden tener mejores ideas sobre cómo abordar esto, pero como estoy usando una representación previa, mis necesidades se están cumpliendo.


Ninguna de estas respuestas parecía lo suficientemente intuitiva, así que creé una pequeña directiva para hacer esto. De esta manera, le permite declarar el título en la página, donde normalmente lo haría, y también le permite ser dinámico.

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

Por supuesto, deberá cambiar el nombre del módulo para que coincida con el suyo.

Para usarlo, simplemente lance esto en su vista, como lo haría con una etiqueta <title> regular:

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

También puede simplemente incluir texto sin formato si no lo necesita dinámicamente:

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

Alternativamente, puede usar un atributo para hacerlo más compatible con IE:

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

Por supuesto, puede poner cualquier texto que desee en la etiqueta, incluido el código Angular. En este ejemplo, buscará $scope.titleText en cualquier controlador en el que se encuentre actualmente la etiqueta de título personalizado.

Solo asegúrate de no tener varias etiquetas de título de página en tu página, o se pegarán unas a otras.

Ejemplo de Plunker aquí http://plnkr.co/edit/nK63te7BSbCxLeZ2ADHV . Tendrá que descargar el archivo zip y ejecutarlo localmente para ver el cambio de título.


Para los escenarios en los que no tiene una ngApp que contenga la etiqueta del title , simplemente inyecte un servicio a los controladores que necesitan establecer el título de la ventana.

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

Ejemplo de trabajo ... http://jsfiddle.net/8m379/1/


Podría definir el controlador en el nivel <html> .

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

Se crea el servicio: Page y modificación de los controladores.

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

Inyecte la Page y llame a ''Page.setTitle ()'' desde los controladores.

Aquí está el ejemplo concreto: http://plnkr.co/edit/0e7T6l


Si no tiene control sobre el elemento del título (como el formulario web asp.net), aquí hay algo que puede usar

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


Solución simplista para angular-ui-router:

HTML:

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

App.js> myApp.config block

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


Tenga en cuenta que también puede establecer el título directamente con javascript, es decir,

$window.document.title = someTitleYouCreated;

Esto no tiene enlace de datos, pero es suficiente cuando poner ng-app en la etiqueta <html> es problemático. (Por ejemplo, utilizando plantillas JSP donde <head> se define en exactamente un lugar, pero tiene más de una aplicación).


Una forma limpia que permite configurar dinámicamente el título o la meta descripción. En el ejemplo, yo uso ui-router pero puedes usar ngRoute de la misma manera.

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


tuvo la mejor respuesta hasta ahora, pero la solución a continuación lo hace ideal (para mí) al agregar los siguientes beneficios:

  • No añade relojes, lo que puede ralentizar las cosas.
  • En realidad automatiza lo que podría haber hecho en el controlador, sin embargo,
  • Todavía me da acceso desde el controlador si todavía lo quiero.
  • Sin inyección extra

En el router:

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

En el bloque de ejecución:

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