Como alterar dinamicamente o cabeçalho com base na exibição parcial do AngularJS?

411

Estou usando a ng-view para incluir visualizações parciais do AngularJS e quero atualizar o título da página e as tags de cabeçalho h1 com base na visualização incluída. Porém, eles estão fora do escopo dos controladores de exibição parcial e, portanto, não consigo descobrir como vinculá-los ao conjunto de dados nos controladores.

Se fosse o ASP.NET MVC, você poderia usar o @ViewBag para fazer isso, mas não sei o equivalente no AngularJS. Eu pesquisei sobre serviços compartilhados, eventos etc., mas ainda não consigo fazê-lo funcionar. Qualquer maneira de modificar meu exemplo para que ele funcione seria muito apreciada.

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

Meu 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
fonte
Este comentário talvez esteja atrasado, mas quero acrescentar. cssfacts.com/simple-dynamic-meta-tags-in-angularjs Isso pode ser útil para definir metas dinâmicas. Você apenas mudará sua meta-variável $ rootScope.
Kamuran Sönecek

Respostas:

342

Você pode definir o controlador no <html>nível.

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

Você cria o serviço: Pagee modifica a partir dos controladores.

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

Injete Pagee chame 'Page.setTitle ()' dos controladores.

Aqui está o exemplo concreto: http://plnkr.co/edit/0e7T6l

Tosh
fonte
11
uhmm ... não tenho certeza se colocar um serviço diretamente no escopo $ é considerado bom na arquitetura do AngularJS. Talvez seja melhor colocar no escopo $ uma função Controller e, em seguida, deixar essa função consultar o serviço.
Superjo
11
Este exemplo foi fantástico. Eu tenho um acompanhamento, no carregamento inicial, você pode ver o texto {{Page.title ()}} no título (muito rapidamente). Eu não acho que você pode usar o ng-manto, pois ele não está no corpo. Alguma sugestão para evitar isso?
Arthur Frankel 17/05
52
Apenas @ArthurFrankel uso ng-bind (por exemplo ng-bind = "Page.Title ()")
Pio Uzamere
2
ou podemos especificar controlador na tag title, sem necessidade de controlador global sobre html cabeçalho: <title NG-controller = "titleCtrl"> {{Page.Title ()}} </ title>
Dmitri Algazin
6
Pessoalmente, prefiro definir o título em $rootScopevez de criar um controlador adicional.
DDA
634

Acabei de descobrir uma ótima maneira de definir o título da sua página se você estiver usando o roteamento:

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 : usando o ng-bindatributo em vez de curvas, {{}}para que não apareçam no carregamento

jkoreska
fonte
11
certo, mas seu exemplo não mostra como alterar o título em $ routeChangeSuccess parametrizado por variáveis ​​de escopo $, como o exemplo de @ tosh usando um serviço Page faz. Então você pode definir, title = "Blog"mas não title = '{{"Blog post " + post.title}}'.
Eric Drechsel
10
@felix você pode acessar título como current.titletambém
Eldelshell
8
$ rootScope.title = atual. $ route.title; sem doble $$
david.sansay 12/09
7
Acabei de atualizar minha versão Angular de várias versões (1.0.5 a 1.2.7) e isso me prejudicou no meu código. Eu estava usando current.$routeo código antigo e estava funcionando. Com a atualização, o dobro de $ na rota é necessário. current.$$route
Tyler Forsythe
6
No atendedor quando pode ver '/Product/:id'. Existe alguma maneira de ter o :idvalor com este método? Eu tentei, title: function(params){return params.id;}mas não funciona ... Talvez usando resolve?
precisa
190

Observe que você também pode definir o título diretamente com javascript, ou seja,

$window.document.title = someTitleYouCreated;

Isso não possui ligação de dados, mas é suficiente quando a inserção ng-appda <html>tag é problemática. (Por exemplo, usando modelos JSP em que <head>é definido exatamente em um local, você possui mais de um aplicativo.)

broc.seib
fonte
5
Esta foi a única maneira de fazê-lo funcionar no Internet Explorer para mim, os outros métodos trabalhou em outros navegadores embora
Maarten
4
Como Maarten mencionado esta é a única abordagem que funciona no IE7 e IE8
Rob
33
Incrível como as pessoas não podem voltar atrás e ver como facilmente essa coisa pode ser feito sem escopos e fábricas
redben
7
Inacreditável. Isso era muito mais simples do que todas as travessuras que os outros estavam mencionando. Obrigado!
Leonard Teo
8
Usar 'janela' simples é bom - isso está agindo diretamente no DOM. '$ window' é uma coisa angular e você precisa injetar para usá-lo. De qualquer maneira funcionará.
Broc.seib
119

Declarar ng-appsobre o htmlelemento fornece escopo raiz para heade body.

Portanto, em seu controlador, injete $rootScopee defina uma propriedade de cabeçalho sobre isso:

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

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

e na sua página:

<title ng-bind="header"></title>
Andy Hitchman
fonte
8
melhor resposta na minha opinião. Ter um controlador no nível do aplicativo ng, conforme descrito na resposta aceita, é inútil neste caso.
Nicolas Janel
1
Eu amo como leve esta solução é, e ele evita usar $$ propriedades
tedwards947
A resposta aceita acrescenta complicações e riscos desnecessários. Esta versão simplifica a configuração de uma variável.
special0ne
3
Se você está decidido a usar $ rootScope, eu pelo menos extraia isso para um serviço, para que você não tenha $ rootScope em seu controlador.
Michael J. Calkins
3
Quero usar esta solução, mas estou curioso para saber quais são as vantagens de usá-la document.title = "App";
Remarsh
43

O módulo angularjs-viewhead mostra um mecanismo para definir o título por visualização usando apenas uma diretiva personalizada.

Ele pode ser aplicado a um elemento de exibição existente cujo conteúdo já é o título da exibição:

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

... ou pode ser usado como um elemento independente. Nesse caso, o elemento ficará invisível no documento renderizado e será usado apenas para definir o título da visualização:

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

O conteúdo desta diretiva é disponibilizado no escopo raiz como viewTitle, para que possa ser usado no elemento title, como qualquer outra variável:

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

Também pode ser usado em qualquer outro local que possa "ver" o escopo raiz. Por exemplo:

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

Esta solução permite que o título seja definido através do mesmo mecanismo usado para controlar o restante da apresentação: Modelos AngularJS. Isso evita a necessidade de desorganizar os controladores com essa lógica de apresentação. O controlador precisa disponibilizar todos os dados que serão usados ​​para informar o título, mas o modelo faz a determinação final de como apresentá-lo e pode usar a interpolação de expressão e os filtros para vincular os dados do escopo normalmente.

(Isenção de responsabilidade: sou o autor deste módulo, mas o referencio aqui apenas na esperança de que ajude alguém a resolver esse problema.)

Martin Atkins
fonte
4
Não posso acreditar que esta solução não tenha sido mais votada. A maioria dos outros são realmente más escolhas de design.
Martin Wawrusch
Concordado, esta deve ser a melhor solução. Eu gosto muito mais disso do que declarar um controlador no nível da página para definir o título. FYI: usando isso com o Angular v1.3.2 e o segmento de rota angular v1.3.3 e está funcionando como um encanto.
Nate Barbettini
Apoio esta solução;)
jkoreska
3
Eu escrevi um pouco mais sobre AngularJS-viewhead e outra idéia relacionada aqui no meu blog: apparently.me.uk/angularjs-view-specific-sidebars
Martin Atkins
Se você reutilizar a mesma exibição na exibição de nível superior e subnível, ainda poderá usar o título da exibição com um ng-if, por exemplo: <h4 ng-if = "$ state.includes ('some-state')" view-title> Detalhes para {{...}} </h4> <h4 ng-if = "! $ state.includes ('some-state')"> Detalhes para {{...}} </ h4 >
anre
32

Aqui está uma solução adaptada que funciona para mim e que não requer injeção de $ rootScope nos controladores para definir títulos de página específicos de recursos.

No modelo mestre:

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

Na configuração de roteamento:

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

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

E no bloco de corrida:

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 no controlador:

function ProductController($scope) {
    //Load product or use resolve in routing
    $scope.page.setTitle($scope.product.name);
}
Mr Hash
fonte
1
O título definido no ProductController ($ scope.page.setTitle) está sendo substituído por $ rootScope. $ On ('$ routeChangeSuccess'. Definir um título padrão em $ rootScope. $ On ('$ routeChangeStart' é mais seguro nesse aspecto.
Kristo Aun
@ Mr-hash: aqui está um pequeno ajuste, eu sugiro, perfeito para projetos angulares existentes com muitas rotas, mas sem títulos. Gerará título a partir do nome do controlador, se nenhum título for definido na rota:$rootScope.page.setTitle(current.$$route.title || current.$$route.controller.replace('Ctrl', ''));
mikhail-t
1
lembre-se de saída sanitize assim:this.title = title.replace('<', '&lt;').replace('>', '&gt;').replace(' & ', ' &amp; ') + ' | Site Name';
Henrik Stenbæk
Recebi um erro indefinido, então mudei o último bit para: $ rootScope.page.title = current. $$ route? current. $$ route.title + '| Nome do site ':' Nome do site ';
Andy
15

A solução da jkoreska é perfeita se você conhece os títulos de antemão, mas pode ser necessário definir o título com base nos dados obtidos de um recurso etc.

Minha solução requer um único serviço. Como o rootScope é a base de todos os elementos DOM, não precisamos colocar um controlador no elemento html como alguém 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 os controladores que precisam alterar o título

app.controller('SomeController', function(Page){
    Page.setTitle("Some Title");
});
Deminetix
fonte
pequeno problema, quando você atualiza uma página, no nome da guia, você vê '{{title}}' e, depois que a página foi renderizada, você vê apenas 'Some Title'. solução com fábrica não tem esse comportamento
Dmitri Algazin
5
ao invés {{title}}usong-bind='title'
Faradox
1
Concordo com @Faradox ... usando ng-bindimpede que a sintaxe pré-interpolada seja exibida antes que o título seja avaliado. +100
Seth
11

Uma maneira limpa que permite definir dinamicamente título ou meta descrição. No exemplo, eu uso o ui-router, mas você pode usar o ngRoute da mesma maneira.

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

Como alternativa, se você estiver usando o ui-router :

index.html

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

Encaminhamento

$stateProvider
  .state('home', {
      url: '/',
      templateUrl: 'views/home.html',
      data: {
        title: 'Welcome Home.'
      }
  }
Nathan Kot
fonte
2
Não consigo fazer isso funcionar. Tenho ui-routeratualização de URL e conteúdo com base no meu estado e não recebo erros ou avisos, mas não consigo acessar nenhuma parte do objeto de configuração do estado $state.current.[...]. Qual versão ui-routervocê usou para fazer isso?
Minha edição "Configuração do tempo de execução" na resposta resolve o problema que mencionei no meu comentário acima. :) Estou aberto a idéias, se houver uma maneira melhor de fazer isso.
isso não funciona para mim e 'title' não foi encontrado nos documentos da API - isso ainda é suportado?
GraehamF
7

Solução personalizada baseada em eventos

Aqui está outra abordagem que não foi mencionada pelos outros aqui (no momento em que escrevo).

Você pode usar eventos personalizados da seguinte forma:

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

Essa abordagem tem a vantagem de não exigir que serviços extras sejam gravados e injetados em todos os controladores que precisam definir o título e também não (ab) usam o $rootScope. Ele também permite que você defina um título dinâmico (como no exemplo de código), o que não é possível usando atributos de dados personalizados no objeto de configuração do roteador (tanto quanto eu saiba).

Michael Bromley
fonte
5

Para cenários em que você não possui um ngApp que contém a titletag, basta injetar um serviço nos controladores que precisam definir o título da janela.

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

Exemplo de trabalho ... http://jsfiddle.net/8m379/1/

JeremyWeir
fonte
5

Se você não tem controle sobre o elemento title (como o formulário da web asp.net), aqui está algo que você pode 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
fonte
4

Maneira simples e suja usando $rootScope:

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

Nos seus controladores, quando você tiver os dados necessários para criar o título, faça:

$rootScope.title = 'Page X'
user1338062
fonte
4

Nenhuma dessas respostas parecia intuitiva o suficiente, então criei uma pequena diretiva para fazer isso. Dessa maneira, você pode declarar o título na página, onde normalmente o faria, e também 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);
        }
    };
});

É claro que você precisará alterar o nome do módulo para corresponder ao seu.

Para usá-lo, basta colocar isso na sua opinião, da mesma forma que faria com uma <title>tag comum :

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

Você também pode incluir apenas texto sem formatação, caso não precise dinamicamente:

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

Como alternativa, você pode usar um atributo, para torná-lo mais amigável ao IE:

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

Você pode colocar o texto que desejar na tag, é claro, incluindo o código Angular. Neste exemplo, ele procurará $scope.titleTextem qualquer controlador em que a tag de título personalizado esteja atualmente.

Apenas certifique-se de não ter várias tags de título de página em sua página, ou elas serão prejudicadas.

Exemplo de plunker aqui http://plnkr.co/edit/nK63te7BSbCxLeZ2ADHV . Você precisará fazer o download do zip e executá-lo localmente para ver a alteração do título.

MikeyB
fonte
Eu vim com algo semelhante. De longe, o mais intuitivo de usar, e não requer a colocação de um controlador html. Na minha diretiva, também injeto uma pageTitlePrefixconstante opcional .
Z0R
4

Solução simplista para roteador-ui angular:

HTML:

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

Bloco 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);
   });
}]);
the_mishra
fonte
3

Aqui está uma maneira diferente de fazer alterações de título. Talvez não seja tão escalável quanto uma função de fábrica (que poderia lidar com páginas ilimitadas), mas era mais fácil para mim entender:

No meu index.html, comecei assim:

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

Então eu fiz um parcial chamado "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>

Então voltei para "index.html" e adicionei o nav.html usando ng-include e ng-view para minhas parciais:

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

Observe que ng-manto? Não tem nada a ver com esta resposta, mas oculta a página até terminar de carregar, com um toque agradável :) Aprenda como aqui: Angularjs - elementos ng-cloak / ng-show piscam

Aqui está o módulo básico. Coloquei-o em um arquivo chamado "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"});
    });

}());

Se você olhar para o final do módulo, verá que tenho uma página de detalhes de bichos baseada em: id. É uma parte parcial usada na página Crispy Critters. [Corny, eu sei - talvez seja um site que celebra todos os tipos de nuggets de frango;) De qualquer forma, você pode atualizar o título quando um usuário clica em qualquer link, portanto, na minha página principal do Crispy Critters que leva à página de detalhes do bicho, é para onde a atualização do $ root.title iria, assim como você viu no nav.html acima:

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

Desculpe muito vento, mas eu prefiro um post que dê detalhes suficientes para colocá-lo em funcionamento. Observe que a página de exemplo nos documentos do AngularJS está desatualizada e mostra uma versão 0.9 do ng-bind-template. Você pode ver que não é tão diferente assim.

Pensamento tardio: você sabe disso, mas está aqui para mais alguém; na parte inferior do index.html, é necessário incluir o app.js no módulo:

        <!-- APP -->
        <script type="text/javascript" src="js/app.js"></script>
    </body>
</html>
noogrub
fonte
2
Minha opinião, não use isso. Você está misturando dados (informações) em visualizações (apresentação). Posteriormente, será muito difícil encontrar fontes de títulos espalhadas por todos os seus links HTML que possam estar presentes em vários lugares da visualização. #
Amit bakle
Como o título é atualizado apenas ao clicar em um link , isso não define o título corretamente quando o usuário chega à página pela primeira vez ou quando é atualizado.
Mark Amery
3

Quando tive que resolver isso, não consegui colocar a tag ng-appna página htmle resolvi-a com um serviço:

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

Solução personalizada baseada em eventos inspirada em Michael Bromley

Não consegui fazê-lo funcionar com $ scope, então tentei com o rootScope, talvez um pouco mais sujo ... (especialmente se você fizer uma atualização na página que não registra o evento)

Mas eu realmente gosto da ideia de como as coisas são vagamente acopladas.

Estou usando o 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>

Está funcionando para mim :)


Thomas
fonte
1

Enquanto outros podem ter métodos melhores, pude usar $ rootScope em meus controladores, pois cada uma das minhas visualizações / modelos possui um controlador distinto. Você precisará injetar o $ rootScope em cada controlador. Embora isso possa não ser o ideal, está funcionando para mim, então achei que deveria passar adiante. Se você inspecionar a página, ela adicionará a vinculação ng à tag title.

Controlador de exemplo:

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

Exemplo de cabeçalho 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>

Você também pode definir o pageTitle e o pageDescription para valores dinâmicos, como retornar dados de uma chamada REST:

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

Novamente, outras pessoas podem ter melhores idéias sobre como abordar isso, mas como estou usando uma pré-renderização, minhas necessidades estão sendo atendidas.

Kode
fonte
0

Obrigado a shoshyama por sua solução.
Eu pensei que não era tão limpo colocar um serviço diretamente no $scope, então aqui está minha pequena variação sobre isso: http://plnkr.co/edit/QJbuZZnZEDOBcYrJXWWs

O controlador (que na resposta original me pareceu um pouco idiota) cria um objeto ActionBar, e este é inserido no $ scope.
O objeto é responsável por realmente consultar o serviço. Ele também oculta do escopo $ a chamada para definir o URL do modelo, que está disponível para outros controladores para definir o URL.

superjos
fonte
0

O Sr. Hash teve a melhor resposta até agora, mas a solução abaixo a torna ideal (para mim) adicionando os seguintes benefícios:

  • Não adiciona relógios, o que pode atrasar as coisas
  • Na verdade, automatiza o que eu poderia ter feito no controlador, ainda
  • Ainda me dá acesso do controlador, se eu ainda o desejar.
  • Nenhuma injeção extra

No roteador:

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

No bloco de execução:

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

A solução melhor e dinâmica que encontrei é usar $ watch para rastrear as alterações das variáveis ​​e, em seguida, atualizar o título.

geólito
fonte