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

411

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 en función de la vista incluida. Sin embargo, estos están fuera del alcance de los controladores de vista parcial, por lo que no puedo entender 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 servicios compartidos, eventos, etc., pero todavía no puedo hacerlo funcionar. 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"; }
mikel
fuente
Este comentario tal vez tarde pero quiero agregar. cssfacts.com/simple-dynamic-meta-tags-in-angularjs Esto puede ser útil para establecer metas dinámicas. Simplemente cambiará su meta variable $ rootScope.
Kamuran Sönecek

Respuestas:

342

Puede definir el controlador en el <html>nivel.

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

Usted crea servicio: Pagey lo modifica desde los controladores.

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

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

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

Tosh
fuente
11
uhmm ... no estoy seguro de si colocar un servicio directamente en el alcance $ se considera agradable en la arquitectura AngularJS. Tal vez podría ser mejor poner en $ scope una función Controller y luego dejar que esta función consulte el servicio.
superjos
11
Este ejemplo fue excelente. Sin embargo, tengo un seguimiento, en la carga inicial puede ver el texto {{Page.title ()}} en el título (muy rápidamente). No creo que puedas usar ng-cloak ya que no está en el cuerpo. ¿Alguna sugerencia para evitar esto?
Arthur Frankel
52
@ArthurFrankel Simplemente use ng-bind (por ejemplo, ng-bind = "Page.title ()")
Pius Uzamere
2
o podemos especificar el controlador en la etiqueta del título, no es necesario un controlador global en el encabezado html: <title ng-controller = "titleCtrl"> {{Page.title ()}} </title>
Dmitri Algazin
66
Personalmente prefiero establecer el título en $rootScopelugar de crear un controlador adicional.
DDA
634

Acabo de descubrir una buena manera 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>
...

Editar : usar el ng-bindatributo en lugar de curlies {{}}para que no se muestren al cargar

jkoreska
fuente
11
correcto, pero su ejemplo no muestra cómo cambiar el título en $ routeChangeSuccess parametrizado por las variables $ scope, lo que sí hace el ejemplo de @ tosh usando un servicio de Página. Entonces puedes configurar title = "Blog"pero no title = '{{"Blog post " + post.title}}'.
Eric Drechsel
10
@felix puedes acceder al título como current.titletambién
Eldelshell
8
$ rootScope.title = actual. $ route.title; sin doble $$
david.sansay
77
Acabo de actualizar mi versión angular varias versiones (1.0.5 a 1.2.7) y esto me rompió en mi código. Estaba usando current.$routeel código anterior y estaba funcionando. Con la actualización, se necesita el doble de $ en ruta. current.$$route
Tyler Forsythe
66
En el contestador cuando puede ver '/Product/:id'. ¿Hay alguna forma de tener el :idvalor con este método? Lo intenté title: function(params){return params.id;}pero no funciona ... ¿Tal vez usando resolve?
mickaelb91
190

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-appla <html>etiqueta es problemático. (Por ejemplo, usando plantillas JSP donde <head>se define exactamente en un lugar, sin embargo, tiene más de una aplicación).

broc.seib
fuente
55
Esta era la única forma de conseguir que funcione en Internet Explorer para mí, los otros métodos trabajaron en otros navegadores, aunque
Maarten
44
Como Maarten mencionó, este es el único enfoque que funciona en ie7 y ie8
robar el
33
Increíble cómo la gente no puede dar un paso atrás y ver lo fácil que esto se puede hacer sin alcances y fábricas
redben
77
Increíble. Esto era mucho más simple que todas las travesuras que otros mencionaban. ¡Gracias!
Leonard Teo
8
Usar una 'ventana' simple está bien, eso está actuando directamente en el DOM. '$ window' es algo angular, y tienes que inyectarlo para usarlo. De cualquier manera funcionará.
broc.seib
119

La declaración ng-appsobre el htmlelemento proporciona un alcance raíz tanto para el heady body.

Por lo tanto, en su controlador, inyecte $rootScopey 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 página:

<title ng-bind="header"></title>
Andy Hitchman
fuente
8
La mejor respuesta en mi opinión. Tener un controlador en el nivel ng-app como se describe en la respuesta aceptada es inútil en este caso.
Nicolas Janel
1
me encanta lo liviana que es esta solución y evita el uso de las propiedades $$
tedwards947
La respuesta aceptada agrega complicaciones y riesgos innecesarios. Esta versión lo hace tan simple como configurar una variable.
special0ne
3
Si está decidido a usar $ rootScope, al menos extraeré esto a un servicio para que no tenga $ rootScope en su controlador.
Michael J. Calkins
3
Quiero usar esta solución, pero tengo curiosidad sobre cuáles son las ventajas de usarla document.title = "App";
remarsh
43

El módulo angularjs-viewhead muestra un mecanismo para establecer el título por 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 puede usarse 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 de título como cualquier otra variable:

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

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

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

Esta solución permite establecer el título mediante el mismo mecanismo que se utiliza para controlar el resto de la presentación: 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 determinación final sobre cómo presentarlo y puede usar la interpolación de expresión y los filtros para enlazar a los datos del alcance de forma 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).

Martin Atkins
fuente
44
No puedo creer que esta solución no haya sido votada más. La mayoría de los otros son realmente malas elecciones de diseño.
Martin Wawrusch
De acuerdo, esta debería ser la mejor solución. Me gusta mucho más que declarar un controlador a nivel de página para configurar el título. FYI: usando esto con Angular v1.3.2 y angular-route-segmento v1.3.3 y está funcionando de maravilla.
Nate Barbettini
Respaldo
3
Escribí un poco más sobre angularjs-viewhead y otra idea relacionada aquí en mi blog: aparentemente.me.uk/angularjs-view-specific-sidebars
Martin Atkins
Si se reutiliza la misma vista en el nivel superior y en la vista de nivel inferior, aún se puede usar view-title con un ng-if, por ejemplo: <h4 ng-if = "$ state.includes ('some-state')" view-title> Detalles para {{...}} </h4> <h4 ng-if = "! $ state.includes ('some-state')"> Detalles para {{...}} </ h4 >
anre
32

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);
}
Mr Hash
fuente
1
El conjunto de títulos en ProductController ($ scope.page.setTitle) está siendo anulado por $ rootScope $ en ( '$ routeChangeSuccess' Ajuste de un título predeterminado en $ $ en rootScope ( '$ routeChangeStart' es más seguro en este sentido....
Kristo Aun
@ mr-hash: aquí sugiero un pequeño ajuste, perfecto para proyectos angulares existentes con muchas rutas, pero sin títulos. $rootScope.page.setTitle(current.$$route.title || current.$$route.controller.replace('Ctrl', ''));
Generará
1
recuerde desinfectar la salida de esta manera:this.title = title.replace('<', '&lt;').replace('>', '&gt;').replace(' & ', ' &amp; ') + ' | Site Name';
Henrik Stenbæk
Obtuve un error indefinido, así que cambié el último bit a: $ rootScope.page.title = current. $$ route? current. $$ route.title + '| Nombre del sitio ':' Nombre del sitio ';
Andy
15

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 obtiene de un recurso, etc.

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

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 de título.

app.controller('SomeController', function(Page){
    Page.setTitle("Some Title");
});
Deminetix
fuente
pequeño problema, cuando actualiza una página, en el nombre de su pestaña, ve '{{title}}' y después de que se renderizó la página, solo ve 'Algún título'. solución con fábrica no tiene ese comportamiento
Dmitri Algazin
55
en {{title}}su lugar useng-bind='title'
Faradox
1
De acuerdo con @Faradox ... el uso ng-bindevita que la sintaxis pre-interpolada se muestre antes de que el título realmente se evalúe. +100
Seth
11

Una forma limpia que permite establecer dinámicamente el título o la meta descripción. En el ejemplo 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>
...
Alex Soroka
fuente
8

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.'
      }
  }
Nathan Kot
fuente
2
No puedo hacer que esto funcione. Tengo ui-routeractualizaciones de URL y contenido según mi estado y no recibo errores ni advertencias, pero parece que no puedo acceder a ninguna parte del objeto de configuración de estado $state.current.[...]. ¿Qué versión de ui-routerusaste para hacer esto?
Mi edición de "Configuración de tiempo de ejecución" a la respuesta resuelve el problema que mencioné en mi comentario anterior. :) Estoy abierto a ideas si hay una mejor manera de hacerlo.
esto no funciona para mí y el "título" no se encuentra en los documentos de la API, ¿esto todavía es compatible?
GraehamF
7

Solución personalizada basada en eventos

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

Puede usar eventos personalizados de esta manera:

// 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) usa el $rootScope. También le permite establecer un título dinámico (como en el ejemplo de código), que no es posible utilizando atributos de datos personalizados en el objeto de configuración del enrutador (hasta donde yo sé al menos).

Michael Bromley
fuente
5

Para escenarios en los que no tiene una ngApp que contiene la titleetiqueta, 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/

JeremyWeir
fuente
5

Si no tiene control sobre el elemento de 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;
        });
    });
Ashish
fuente
4

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 lo siguiente:

$rootScope.title = 'Page X'
usuario1338062
fuente
4

Ninguna de estas respuestas parecía lo suficientemente intuitiva, así que creé una pequeña directiva para hacer esto. De esta manera, puede 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 arroje esto a su vista, como lo haría con una <title>etiqueta normal :

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

También puede 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 amigable con IE:

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

Por supuesto, puede poner el texto que desee en la etiqueta, incluido el código angular. En este ejemplo, buscará $scope.titleTexten 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 golpearán entre sí.

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

MikeyB
fuente
Se me ocurrió algo similar. Con mucho, el más intuitivo de usar, y no requiere poner un controlador html. En mi directiva también inyecto una pageTitlePrefixconstante opcional .
z0r
4

Solución simplista para angular-ui-router:

HTML:

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

App.js> bloque 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);
   });
}]);
the_mishra
fuente
3

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

En mi index.html comencé 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é el 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>

¿Te das cuenta de que ng-cloak? No tiene nada que ver con esta respuesta, pero oculta la página hasta que termine de cargarse, un toque agradable :) Aprenda cómo aquí: Angularjs - parpadean los elementos ng-cloak / ng-show

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 mira hacia el final del módulo, verá que tengo una página de detalles de criaturas basada en: id. Es un parcial que se usa desde la página Crispy Critters. [Corny, lo sé, tal vez es un sitio que celebra todo tipo de nuggets de pollo;) De todos modos, puedes actualizar el título cuando un usuario hace clic en cualquier enlace, así que en mi página principal de Crispy Critters que lleva a la página de detalles de criaturas, ahí es donde iría la actualización de $ root.title, tal como viste en el 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 brinde suficientes detalles para ponerla en marcha. 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: sabes esto pero está aquí para cualquier otra persona; en la parte inferior de index.html, uno debe incluir app.js con el módulo:

        <!-- APP -->
        <script type="text/javascript" src="js/app.js"></script>
    </body>
</html>
noogrub
fuente
2
Mi opinión, no uses esto. Está mezclando datos (información) en vistas (presentación). Más adelante será muy difícil encontrar fuentes de títulos dispersas por todos los enlaces HTML que puedan estar presentes en varios lugares de la vista ...
amit bakle
Debido a que el título solo se actualiza al hacer clic en un enlace , esto no establece el título correctamente cuando el usuario llega por primera vez a una página o cuando el usuario actualiza.
Mark Amery
3

Cuando tuve que resolver esto, no pude colocarlo ng-appen la htmletiqueta de 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;
        }
    };

});
Tom Söderlund
fuente
2

Solución personalizada basada en eventos inspirada en Michael Bromley

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

Pero realmente me gusta la idea de cómo las cosas están unidas 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>

Me está funcionando :)


Thomas
fuente
1

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 $ rootScope en cada controlador. Si bien esto puede no ser ideal, está funcionando para mí, así que pensé que debería pasarlo. Si inspecciona la página, agrega el enlace ng a la etiqueta del título.

Controlador de ejemplo:

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 establecer pageTitle y pageDescription en valores dinámicos, como devolver datos de 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;
});

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

Kode
fuente
0

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

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

superjos
fuente
0

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

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

En el enrutador:

  .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
])
Kim Miller
fuente
-4

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

geolyth
fuente