Come modificare dinamicamente l'intestazione in base alla vista parziale AngularJS?


411

Sto usando ng-view per includere le viste parziali di AngularJS e voglio aggiornare il titolo della pagina e i tag dell'intestazione h1 in base alla vista inclusa. Questi non rientrano nell'ambito dei controller della vista parziale, quindi non riesco a capire come collegarli ai set di dati nei controller.

Se fosse ASP.NET MVC potresti usare @ViewBag per farlo, ma non conosco l'equivalente in AngularJS. Ho cercato servizi condivisi, eventi ecc. Ma non riesco ancora a farlo funzionare. Qualsiasi modo di modificare il mio esempio in modo che funzioni sarebbe molto apprezzato.

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

Il mio 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"; }

Questo commento forse in ritardo, ma voglio aggiungere. cssfacts.com/simple-dynamic-meta-tags-in-angularjs Questo può essere utile per impostare metas dinamici. Cambierai semplicemente la tua meta variabile $ rootScope.
Kamuran Sönecek,

Risposte:


342

È possibile definire il controller a <html>livello.

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

Crea servizio: Pagee modifica dai controller.

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

Iniettare Pagee chiamare 'Page.setTitle ()' dai controller.

Ecco l'esempio concreto: http://plnkr.co/edit/0e7T6l


11
uhmm ... non sono sicuro che posizionare un servizio direttamente nell'ambito $ scope sia considerato carino nell'architettura AngularJS. Forse potrebbe essere meglio mettere in $ scope una funzione Controller e quindi lasciare che questa funzione interroghi il servizio.
superjos

11
Questo esempio è stato formidabile. Ho un follow-up, tuttavia, al caricamento iniziale puoi vedere il testo {{Page.title ()}} nel titolo (molto rapidamente). Non penso che tu possa usare ng-cloak poiché non è nel corpo. Qualche suggerimento per evitarlo?
Arthur Frankel,

52
@ArthurFrankel Basta usare ng-bind (es. Ng-bind = "Page.title ()")
Pius Uzamere

2
oppure possiamo specificare il controller nel tag del titolo, non è necessario un controller globale nell'intestazione html: <title ng-controller = "titleCtrl"> {{Page.title ()}} </title>
Dmitri Algazin

6
Personalmente preferisco impostare il titolo $rootScopeanziché creare un controller aggiuntivo.
DDA,

634

Ho appena scoperto un bel modo per impostare il titolo della tua pagina se stai utilizzando il routing:

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

Modifica : utilizzando l' ng-bindattributo anziché i ricci in {{}}modo che non vengano visualizzati al caricamento


11
giusto, ma il tuo esempio non mostra come cambiare il titolo su $ routeChangeSuccess parametrizzato da variabili $ scope, cosa che fa l'esempio di @ tosh usando un servizio Pagina. Quindi puoi impostare title = "Blog"ma non title = '{{"Blog post " + post.title}}'.
Eric Drechsel,

10
@felix puoi accedere al titolo come current.titleanche
Eldelshell il

8
$ rootScope.title = current. $ route.title; senza doble $$
david.sansay

7
Ho appena aggiornato la mia versione angolare diverse versioni (da 1.0.5 a 1.2.7) e questo mi ha rotto nel mio codice. Stavo usando current.$routeil vecchio codice e funzionava. Con l'aggiornamento, è necessario il doppio $ sulla rotta. current.$$route
Tyler Forsythe,

6
Nel risponditore quando può vedere '/Product/:id'. C'è un modo per avere il :idvalore con questo metodo? Ho provato title: function(params){return params.id;}ma non funziona ... Forse stai usando resolve?
mickaelb91,

190

Tieni presente che puoi anche impostare il titolo direttamente con JavaScript, ovvero

$window.document.title = someTitleYouCreated;

Questo non ha un'associazione di dati, ma è sufficiente quando inserire ng-appil <html>tag è problematico. (Ad esempio, utilizzando i modelli JSP in cui <head>è definito esattamente in un posto, ma hai più di un'app.)


5
Questo è stato l'unico modo per farlo funzionare su Internet Explorer per me, ma gli altri metodi hanno funzionato su altri browser
Maarten,

4
Come ha detto Maarten, questo è l'unico approccio che funziona in ie7 e ie8
rob

33
Incredibile come le persone non possano fare un passo indietro e vedere con quanta facilità si possa fare questa cosa senza scopi e fabbriche
redben

7
Incredibile. Questo era molto più semplice di tutti gli shenanigani che altri stavano citando. Grazie!
Leonard Teo,

8
Usare la semplice 'finestra' va bene - questo agisce direttamente sul DOM. '$ window' è una cosa angolare e devi iniettarla per usarla. In entrambi i casi funzionerà.
broc.seib,

119

Dichiarando ng-appsul htmlelemento prevede la possibilità radice sia per l' heade body.

Pertanto nel controller iniettare $rootScopee impostare una proprietà di intestazione su questo:

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

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

e nella tua pagina:

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

8
migliore risposta a mio parere. In questo caso è inutile avere un controller a livello di ng-app come descritto nella risposta accettata.
Nicolas Janel,

1
Adoro la leggerezza di questa soluzione ed evita l'utilizzo delle proprietà $$
tedwards947

La risposta accettata aggiunge complicazioni e rischi inutili. Questa versione semplifica l'impostazione di una variabile.
special0ne

3
Se non hai impostato $ rootScope, lo estrarrei almeno in un servizio in modo da non avere $ rootScope nel controller.
Michael J. Calkins,

3
Voglio usare questa soluzione ma sono curioso di sapere quali sono i vantaggi dell'utilizzo document.title = "App";
rimessa in servizio il

43

Il modulo angularjs-viewhead mostra un meccanismo per impostare il titolo in base alla vista usando solo una direttiva personalizzata.

Può essere applicato a un elemento vista esistente il cui contenuto è già il titolo vista:

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

... oppure può essere utilizzato come elemento autonomo, nel qual caso l'elemento sarà invisibile nel documento renderizzato e verrà utilizzato solo per impostare il titolo della vista:

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

Il contenuto di questa direttiva è reso disponibile nell'ambito root come viewTitle, quindi può essere usato sull'elemento title come qualsiasi altra variabile:

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

Può anche essere utilizzato in qualsiasi altro punto in grado di "vedere" l'ambito radice. Per esempio:

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

Questa soluzione consente di impostare il titolo tramite lo stesso meccanismo utilizzato per controllare il resto della presentazione: modelli AngularJS. Ciò evita la necessità di ingombrare i controller con questa logica di presentazione. Il responsabile del trattamento deve rendere disponibili tutti i dati che verranno utilizzati per informare il titolo, ma il modello prende la decisione finale su come presentarlo e può utilizzare l'interpolazione delle espressioni e i filtri per associare i dati dell'ambito come di consueto.

(Dichiarazione di non responsabilità: sono l'autore di questo modulo, ma sto facendo riferimento qui solo nella speranza che possa aiutare qualcun altro a risolvere questo problema.)


4
Non riesco a credere che questa soluzione non sia stata più votata. La maggior parte delle altre sono scelte di design davvero pessime.
Martin Wawrusch,

D'accordo, questa dovrebbe essere la soluzione migliore. Mi piace molto di più che dichiarare un controller a livello di pagina per l'impostazione del titolo. Cordiali saluti: usando questo con Angular v1.3.2 e angular-route-segment v1.3.3 e funziona come un incantesimo.
Nate Barbettini,


3
Ho scritto qualcosa in più su angularjs-viewhead e un'altra idea correlata qui sul mio blog: apparently.me.uk/angularjs-view-specific-sidebars
Martin Atkins

Se riutilizzi la stessa vista di livello superiore e di livello inferiore, puoi comunque usare view-title con un ng-if, ad esempio: <h4 ng-if = "$ state.includes ('some-state')" view-title> Dettagli per {{...}} </h4> <h4 ng-if = "! $ state.includes ('some-state')"> Dettagli per {{...}} </ h4 >
anre

32

Ecco una soluzione adattata che funziona per me che non richiede l'iniezione di $ rootScope nei controller per l'impostazione di titoli di pagina specifici delle risorse.

Nel modello principale:

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

Nella configurazione del routing:

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

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

E nel blocco di esecuzione:

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 nel controller:

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

1
Il titolo impostato in ProductController ($ scope.page.setTitle) è stato sostituito da $ rootScope. $ On ('$ routeChangeSuccess'. L'impostazione di un titolo predefinito in $ rootScope. $ On ('$ routeChangeStart' è più sicuro in questo senso.
Kristo Aun

@ mr-hash: ecco un piccolo aggiustamento che suggerisco, perfetto per progetti angolari esistenti con molti percorsi, ma senza titoli. Genera titolo dal nome del controller, se nessun titolo è definito sulla rotta:$rootScope.page.setTitle(current.$$route.title || current.$$route.controller.replace('Ctrl', ''));
mikhail-t

1
ricordati di disinfettare l'output in questo modo:this.title = title.replace('<', '&lt;').replace('>', '&gt;').replace(' & ', ' &amp; ') + ' | Site Name';
Henrik Stenbæk,

Ho ricevuto un errore indefinito, quindi ho cambiato l'ultimo bit in: $ rootScope.page.title = current. $$ route? corrente. $$ route.title + '| Nome sito ":" Nome sito ";
Andy,

15

La soluzione di jkoreska è perfetta se conosci i titoli in anticipo, ma potresti dover impostare il titolo in base ai dati che ottieni da una risorsa, ecc.

La mia soluzione richiede un singolo servizio. Poiché rootScope è la base di tutti gli elementi DOM, non abbiamo bisogno di mettere un controller sull'elemento html come qualcuno menzionato

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

Tutti i controller che devono cambiare titolo

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

piccolo problema, quando si aggiorna una pagina, nel nome della scheda viene visualizzato "{{title}}" e dopo il rendering della pagina, viene visualizzato solo "Qualche titolo". la soluzione con la fabbrica non ha questo comportamento
Dmitri Algazin

5
invece {{title}}usang-bind='title'
Faradox il

1
Accetto con @Faradox ... l'utilizzo ng-bindimpedisce la visualizzazione della sintassi pre-interpolata prima che il titolo venga effettivamente valutato. +100
Seth

11

Un modo pulito che consente di impostare dinamicamente il titolo o la meta descrizione. Ad esempio utilizzo ui-router ma puoi usare ngRoute allo stesso modo.

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

8

In alternativa, se si utilizza ui-router :

index.html

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

Routing

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

2
Non riesco a farlo funzionare. Ho l' ui-routeraggiornamento dell'URL e del contenuto in base al mio stato e non ricevo errori o avvisi, ma non riesco ad accedere a nessuna parte dell'oggetto di configurazione dello stato $state.current.[...]. Quale versione di ui-routerhai usato per fare questo?

La mia modifica "Runtime Config" alla risposta risolve il problema che ho citato nel mio commento sopra. :) Sono aperto alle idee se c'è un modo migliore per farlo.

questo non funziona per me e il "titolo" non si trova nei documenti API - è ancora supportato?
GraehamF,

7

Soluzione personalizzata basata su eventi

Ecco un altro approccio che non è stato menzionato dagli altri qui (al momento della stesura di questo documento).

È possibile utilizzare eventi personalizzati in questo modo:

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

Questo approccio ha il vantaggio di non richiedere che servizi aggiuntivi vengano scritti e quindi iniettati in tutti i controller che devono impostare il titolo, e inoltre non (ab) usa $rootScope. Consente inoltre di impostare un titolo dinamico (come nell'esempio di codice), che non è possibile utilizzando attributi di dati personalizzati sull'oggetto di configurazione del router (per quanto ne so almeno).


5

Per gli scenari in cui non hai una ngApp che contiene il titletag, basta iniettare un servizio ai controller che devono impostare il titolo della finestra.

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

Esempio di lavoro ... http://jsfiddle.net/8m379/1/


5

Se non hai il controllo sull'elemento del titolo (come il modulo web asp.net) ecco alcune cose che puoi usare

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

4

Modo semplice e sporco usando $rootScope:

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

Nei tuoi controller, quando hai i dati necessari per creare il titolo, fai:

$rootScope.title = 'Page X'

4

Nessuna di queste risposte sembrava abbastanza intuitiva, quindi ho creato una piccola direttiva per farlo. In questo modo ti permette di dichiarare il titolo nella pagina, dove normalmente lo si farebbe, e anche di essere dinamico.

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

Ovviamente dovrai cambiare il nome del modulo in modo che corrisponda al tuo.

Per usarlo, basta lanciarlo nella tua vista, proprio come faresti per un <title>tag normale :

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

Puoi anche includere solo testo normale se non ne hai bisogno per dinamica:

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

In alternativa, puoi utilizzare un attributo per renderlo più intuitivo per IE:

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

Ovviamente puoi inserire qualsiasi testo nel tag, incluso il codice angolare. In questo esempio, cercherà $scope.titleTextin qualunque controller sia presente il tag del titolo personalizzato.

Assicurati solo di non avere più tag di titolo di pagina sulla tua pagina, altrimenti si ostruiranno l'un l'altro.

Esempio di Plunker qui http://plnkr.co/edit/nK63te7BSbCxLeZ2ADHV . Dovrai scaricare lo zip ed eseguirlo localmente per vedere la modifica del titolo.


Ho pensato a qualcosa di simile. Di gran lunga il più intuitivo da usare e non richiede di mettere un controller html. Nella mia direttiva ho anche iniettato una pageTitlePrefixcostante opzionale .
z0r

4

Soluzione semplicistica per angular-ui-router:

HTML:

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

App.js> blocco 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);
   });
}]);

3

Ecco un modo diverso di apportare modifiche al titolo. Forse non è scalabile come una funzione di fabbrica (che potrebbe concepibilmente gestire pagine illimitate) ma è stato più facile per me capire:

Nel mio index.html ho iniziato così:

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

Quindi ho creato un parziale chiamato "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>

Quindi sono tornato a "index.html" e ho aggiunto nav.html usando ng-include e ng-view per i miei parziali:

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

Notate quel ng-mantello? Non ha nulla a che fare con questa risposta, ma nasconde la pagina fino al termine del caricamento, un bel tocco :) Scopri come qui: Angularjs - ng-cloak / ng-show elements lampeggia

Ecco il modulo base. L'ho inserito in un file chiamato "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 guardi verso la fine del modulo, vedrai che ho una pagina con i dettagli delle creature basata su: id. È un parziale che viene utilizzato dalla pagina Crispy Critters. [Corny, lo so - forse è un sito che celebra tutti i tipi di crocchette di pollo;) Ad ogni modo, potresti aggiornare il titolo quando un utente fa clic su qualsiasi collegamento, quindi nella mia pagina Crispy Critters principale che porta alla pagina dei dettagli delle critter, ecco dove andrebbe l'aggiornamento $ root.title, proprio come hai visto nel file nav.html sopra:

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

Mi dispiace così ventoso, ma preferisco un post che fornisca dettagli sufficienti per farlo funzionare. Si noti che la pagina di esempio nei documenti AngularJS non è aggiornata e mostra una versione 0.9 di ng-bind-template. Puoi vedere che non è molto diverso.

Ripensamento: lo sai ma è qui per chiunque altro; nella parte inferiore di index.html, è necessario includere app.js con il modulo:

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

2
La mia opinione, non usare questo. Stai mescolando i dati (informazioni) nelle viste (presentazione). In seguito sarà molto difficile trovare fonti di titoli sparse in tutti i tuoi collegamenti HTML che potrebbero essere presenti in vari punti della vista.
bakle,

Poiché il titolo viene aggiornato solo facendo effettivamente clic su un collegamento , ciò non imposta il titolo correttamente quando l'utente arriva per la prima volta su una pagina o quando l'utente aggiorna.
Mark Amery,

3

Quando ho dovuto risolvere questo problema, non sono riuscito a posizionare il tag ng-appsulla pagina html, quindi l'ho risolto con un servizio:

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

});

2

Soluzione basata su eventi personalizzati ispirata a Michael Bromley

Non sono riuscito a farlo funzionare con $ scope, quindi ho provato con rootScope, forse un po 'più sporco ... (specialmente se fai un aggiornamento sulla pagina che non registra l'evento)

Ma mi piace molto l'idea di come le cose siano liberamente accoppiate.

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

Funziona per me :)



1

Mentre altri potrebbero avere metodi migliori, sono stato in grado di utilizzare $ rootScope nei miei controller, poiché ciascuna delle mie viste / modelli ha un controller distinto. Sarà necessario iniettare $ rootScope in ciascun controller. Anche se questo potrebbe non essere l'ideale, funziona per me, quindi ho pensato di doverlo passare. Se si ispeziona la pagina, si aggiunge ng-binding al tag del titolo.

Controller di esempio:

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

Intestazione Index.html di esempio:

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

È inoltre possibile impostare pageTitle e pageDescription su valori dinamici, ad esempio la restituzione dei dati da una chiamata REST:

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

Ancora una volta, altri potrebbero avere idee migliori su come affrontare questo, ma poiché sto usando un pre-rendering, i miei bisogni sono stati soddisfatti.


0

Grazie a tosh shimayama per la sua soluzione.
Pensavo che non fosse così pulito inserire un servizio direttamente in $scope, quindi ecco la mia leggera variazione al riguardo: http://plnkr.co/edit/QJbuZZnZEDOBcYrJXWWs

Il controller (che nella risposta originale mi è sembrato un po 'troppo stupido) crea un oggetto ActionBar, e questo è inserito in $ scope.
L'oggetto è responsabile dell'interrogazione effettiva del servizio. Inoltre nasconde da $ scope la chiamata per impostare l'URL del modello, che invece è disponibile per altri controller per impostare l'URL.


0

Finora Hash ha avuto la migliore risposta, ma la soluzione che segue la rende ideale (per me) aggiungendo i seguenti vantaggi:

  • Non aggiunge orologi, il che può rallentare le cose
  • In realtà automatizza ancora ciò che avrei potuto fare nel controller
  • Mi dà ancora accesso dal controller se lo voglio ancora.
  • Nessuna iniezione extra

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

Nel blocco di esecuzione:

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

-4

La soluzione migliore e dinamica che ho trovato è usare $ watch per tracciare le modifiche alle variabili e quindi aggiornare il titolo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.