Imposta il titolo della pagina utilizzando UI-Router


101

Sto migrando la mia app basata su AngularJS per utilizzare ui-router invece del routing integrato. L'ho configurato come mostrato di seguito

.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/home');
$stateProvider
    .state('home', {
        url: '/home',
        templateUrl : 'views/home.html',
        data : { pageTitle: 'Home' }

    })
    .state('about', {
        url: '/about',
        templateUrl : 'views/about.html',
        data : { pageTitle: 'About' }
    })
     });

Come posso utilizzare la variabile pageTitle per impostare dinamicamente il titolo della pagina? Usando il routing integrato, potrei farlo

$rootScope.$on("$routeChangeSuccess", function(currentRoute, previousRoute){
    $rootScope.pageTitle = $route.current.data.pageTitle;
  });

e quindi associare la variabile in HTML come mostrato di seguito

<title ng-bind="$root.pageTitle"></title>

C'è un evento simile a cui posso agganciarmi usando ui-router? Ho notato che ci sono funzioni "onEnter" e "onExit" ma sembrano essere legate a ogni stato e mi richiederanno di ripetere il codice per impostare la variabile $ rootScope per ogni stato.


3
C'è un evento $ stateChangeSuccess.
Jerrad,

Risposte:


108

Usa $stateChangeSuccess.

Puoi metterlo in una direttiva:

app.directive('updateTitle', ['$rootScope', '$timeout',
  function($rootScope, $timeout) {
    return {
      link: function(scope, element) {

        var listener = function(event, toState) {

          var title = 'Default Title';
          if (toState.data && toState.data.pageTitle) title = toState.data.pageTitle;

          $timeout(function() {
            element.text(title);
          }, 0, false);
        };

        $rootScope.$on('$stateChangeSuccess', listener);
      }
    };
  }
]);

E:

<title update-title></title>

Demo: http://run.plnkr.co/8tqvzlCw62Tl7t4j/#/home

Codice: http://plnkr.co/edit/XO6RyBPURQFPodoFdYgX?p=preview

Anche con $stateChangeSuccessil $timeoutè stato necessario perché la storia fosse corretta, almeno quando ho messo alla prova me stesso.


Modifica: 24 novembre 2014 - Approccio dichiarativo:

app.directive('title', ['$rootScope', '$timeout',
  function($rootScope, $timeout) {
    return {
      link: function() {

        var listener = function(event, toState) {

          $timeout(function() {
            $rootScope.title = (toState.data && toState.data.pageTitle) 
            ? toState.data.pageTitle 
            : 'Default title';
          });
        };

        $rootScope.$on('$stateChangeSuccess', listener);
      }
    };
  }
]);

E:

<title>{{title}}</title>

Demo: http://run.plnkr.co/d4s3qBikieq8egX7/#/credits

Codice: http://plnkr.co/edit/NpzQsxYGofswWQUBGthR?p=preview


Super fantastico. Non potrebbe essere più facile
Matthew Harwood

3
Anche questo esempio non funziona correttamente con la cronologia (almeno in Chrome 37). Se passi da uno stato all'altro, guarda la tua cronologia, il titolo dell'elemento della cronologia sarà il valore della pagina precedente. Se vai a pagina1 -> pagina2 -> pagina3, quindi guarda la cronologia, l'URL di pagina2 verrà abbinato al titolo di pagina1.
jkjustjoshing

2
In realtà, non è del tutto preciso. Il titolo della pagina cambia prima che cambi l'hash dell'URL, quindi il browser pensa che il nuovo titolo sia per la vecchia pagina. La cronologia del pulsante Indietro viene quindi disattivata di 1 pagina. Avvolgere la element.text(title)chiamata in un $ timeout ha funzionato per me. Modifica del post originale.
jkjustjoshing

3
Questo non funzionerà se il titolo deve essere dinamico in base ad alcuni parametri URL.
Kushagra Gour

10
@KushagraGour Se le esigenze titolo da dinamico in base ai $ stateParams, è possibile utilizzare una funzione in resolveper generare, quindi accedere al valore "risolto" durante l'evento stateChangeSuccess $ con: $state.$current.locals.resolve.$$values.NAME_OF_RESOLVE_FUNCTION.
Claus Conrad

91

C'è un altro modo per farlo combinando già la maggior parte delle risposte qui. So che ha già una risposta, ma volevo mostrare il modo in cui cambio dinamicamente i titoli delle pagine con ui-router.

Se dai un'occhiata all'app di esempio ui-router , usano il blocco angular .run per aggiungere la variabile $ state a $ rootScope.

// It's very handy to add references to $state and $stateParams to the $rootScope
// so that you can access them from any scope within your applications.
// For example, <li ng-class="{ active: $state.includes('contacts.list') }"> 
// will set the <li> to active whenever 'contacts.list' or one of its 
// decendents is active.

.run([ '$rootScope', '$state', '$stateParams',
function ($rootScope, $state, $stateParams) {
  $rootScope.$state = $state;
  $rootScope.$stateParams = $stateParams;
}])

Con questo definito puoi quindi aggiornare facilmente in modo dinamico il titolo della pagina con ciò che hai pubblicato ma modificato per utilizzare lo stato definito:

Imposta lo stato allo stesso modo:

.state('home', {
    url: '/home',
    templateUrl : 'views/home.html',
    data : { pageTitle: 'Home' }
})

Ma modifica un po 'l'html ...

<title ng-bind="$state.current.data.pageTitle"></title>

Non posso dire che questo sia migliore delle risposte precedenti ... ma è stato più facile per me capire e implementare. Spero che questo aiuti qualcuno!


3
Più dichiarativa della risposta accettata. Mi piace questo!
Endy Tjahjono

3
Non sono sicuro se vorrei l'intero oggetto $ scope in $ rootScope solo per il titolo della pagina ...
Jesús Carrera

Non sono sicuro di dove venga fatto riferimento all'oggetto $ scope @ JesúsCarrera
cwbutler

Ups, mi dispiace intendo l'oggetto $ state
Jesús Carrera

4
solo per confermare github.com/angular-ui/ui-router/wiki/Quick-Reference raccomanda inoltre di impostazione $statee $stateParamssulla $rootScopedall'interno run.
Mark Peterson

17

Il plug -in angular-ui-router-title semplifica l'aggiornamento del titolo della pagina a un valore statico o dinamico in base allo stato corrente. Funziona correttamente anche con la cronologia del browser.


Questa sembra essere la migliore soluzione per il futuro. Ho notato diverse incongruenze con la cronologia del browser utilizzando alcune delle altre soluzioni in questa pagina.

angular-ui-router-title sembra essere la soluzione migliore. Soprattutto è senza problemi! Grazie Stepan.
Dário

È un file sorgente molto piccolo.
Tyler Collier

15

$stateChangeSuccessè ora deprecato in UI-Router 1.xe disabilitato per impostazione predefinita. Ora dovrai utilizzare il nuovo $transitionservizio.

Una soluzione non è troppo difficile una volta capito come $transitionfunziona. Ho ricevuto un aiuto da @troig per capire tutto. Ecco cosa mi è venuto in mente per aggiornare il titolo.

Inseriscilo nella tua applicazione Angular 1.6. Nota che sto usando la sintassi ECMAScript 6; se non lo sei, dovrai ad esempio passare leta var.

.run(function($transitions, $window) {
    $transitions.onSuccess({}, (transition) => {
        let title = transition.to().title;
        if (title) {
            if (title instanceof Function) {
                title = title.call(transition.to(), transition.params());
            }
            $window.document.title = title;
        }
    });

Quindi aggiungi una titlestringa al tuo stato:

$stateProvider.state({
    name: "foo",
    url: "/foo",
    template: "<foo-widget layout='row'/>",
    title: "Foo Page""
});

Questo farà apparire le parole "Foo Page" nel titolo. (Se uno stato non ha titolo, il titolo della pagina non verrà aggiornato. Sarebbe una cosa semplice aggiornare il codice sopra per fornire un titolo predefinito se uno stato non ne indica uno.)

Il codice consente anche di utilizzare una funzione per title. L' thisusato per chiamare la funzione sarà lo stato stesso e l'unico argomento saranno i parametri di stato, come questo esempio:

$stateProvider.state({
    name: "bar",
    url: "/bar/{code}",
    template: "<bar-widget code='{{code}}' layout='row'/>",
    title: function(params) {
        return `Bar Code ${params.code}`;
    }
});

Per il percorso dell'URL /bar/code/123 che mostrerebbe "Codice a barre 123" come titolo della pagina. Nota che sto usando la sintassi ECMAScript 6 per formattare la stringa ed estrarla params.code.

Sarebbe bello se qualcuno che avesse il tempo inserisse qualcosa del genere in una direttiva e la pubblicasse affinché tutti possano usarla.


Usa dataoggetto per chiavi personalizzate. titlenon esiste StateDeclarationsull'interfaccia.
Gaui

5

Collegamento di $ state a $ rootscope da utilizzare ovunque nell'app.

app.run(['$rootScope', '$state', '$stateParams',
    function ($rootScope,   $state,   $stateParams) {

        // It's very handy to add references to $state and $stateParams to the $rootScope
        // so that you can access them from any scope within your applications.For example,
        // <li ng-class="{ active: $state.includes('contacts.list') }"> will set the <li>
        // to active whenever 'contacts.list' or one of its decendents is active.
        $rootScope.$state = $state;
        $rootScope.$stateParams = $stateParams;
    }
  ]
)
<title ng-bind="$state.current.name + ' - ui-router'">about - ui-router</title>


1
Combinato con l'aggiunta di un titolo a ogni stato. Funziona perfettamente.
WCByrne

5

Ho trovato questo modo davvero facile:

  .state('app.staff.client', {
    url: '/client/mine',
    title: 'My Clients'})

e poi nel mio HTML in questo modo:

<h3>{{ $state.current.title }}</h3>

5

Basta aggiornare window.document.title:

.state('login', {
   url: '/login',
   templateUrl: "/Login",
   controller: "loginCtrl",
   onEnter: function($window){$window.document.title = "App Login"; }
})

In questo modo "ng-app" non ha bisogno di spostarsi verso l'alto nel tag HTML e può rimanere sul corpo o inferiore.


1
Perché non è questa la risposta migliore? = /
rex

3

Sto usando ngMeta , che funziona bene non solo per impostare il titolo della pagina ma anche per le descrizioni. Ti consente di impostare un titolo / descrizione specifico per ogni stato, i valori predefiniti per quando un titolo / descrizione non è specificato, così come i suffissi del titolo predefiniti (ad esempio, '| MySiteName') e il valore dell'autore.

$stateProvider
  .state('home', {
    url: '/',
    templateUrl: 'views/home.html',
    controller: 'HomeController',
    meta: {
      'title': 'Home',
      'titleSuffix': ' | MySiteName',
      'description': 'This is my home page description lorem ipsum.'
    },
  })

2

In realtà sei molto vicino alla tua prima risposta / domanda. Aggiungi il tuo titolo come oggetto dati:

.state('home', {
    url: '/home',
    templateUrl : 'views/home.html',
    data : { pageTitle: 'Home' }
})

Nel tuo index.html associa i dati direttamente al titolo della pagina:

<title data-ng-bind="$state.current.data.pageTitle + ' - Optional text'">Failsafe text</title>

1

Ho finito con questa combinazione di risposte di Martin e tasseKATT - semplice e senza materiale relativo ai modelli:

$rootScope.$on("$stateChangeSuccess", function (event, toState) {
   $timeout(function () { // Needed to ensure the title is changed *after* the url so that history entries are correct.
     $window.document.title = toState.name; 
   });
});

se non ci sono elementi relativi ai modelli, come farebbe un nuovo sviluppatore a sapere come è stato cambiato il titolo senza chiedere come è stato cambiato?
ton.yeung

se usi $ window.document.title $ timeout è inutile. Sto seguendo questo hackish solo per sbarazzarmi di $ timeout e un $ digest cycle :)
Whisher

1

Perché non solo:

$window.document.title = 'Title';

AGGIORNAMENTO: Codice Direttiva completo

var DIRECTIVE = 'yourPageTitle';

yourPageTitle.$inject = ['$window'];
function yourPageTitle($window: ng.IWindowService): ng.IDirective {

    return {
        link: (scope, element, attrs) => {

            attrs.$observe(DIRECTIVE, (value: string) => {

                $window.document.title = value;
            });
        }
    }
}

directive(DIRECTIVE, yourPageTitle);

Quindi in ogni pagina dovresti semplicemente includere questa direttiva:

<section
    your-page-title="{{'somePage' | translate}}">

potrebbe essere molto difficile scoprire perché / come il titolo sta cambiando per chiunque erediti la base di codice
ton.yeung

Perché sarebbe difficile scoprirlo? Questo frammento dovrebbe essere attivato da una direttiva, ad esempio your-page-titile = "{{'pageTitle' | translate}}. Questa direttiva verrebbe inclusa nel primo elemento di ogni pagina. Bello e dichiarativo chiaro.
Martin

oh, con la modifica, capisco cosa intendi ora. quello che volevo dire era che l'unico liner poteva potenzialmente essere messo ovunque, $ rootscope, direttiva, ecc ...
ton.yeung

0

Se stai usando ES6, funziona benissimo :).

class PageTitle {
    constructor($compile, $timeout) {
        this.restrict = 'A';
        this._$compile = $compile;
        this.$timeout = $timeout;
    }

    compile(element) {
        return this.link.bind(this);
    }

    link(scope, element, attrs, controller) {
        let defaultTitle = attrs.pageTitle ? attrs.pageTitle : "My Awesome Sauce Site";
        let listener = function(event, toState) {
            let title = defaultTitle;
            if (toState.data && toState.data.title) title = toState.data.title + ' | ' + title;
            $('html head title').text(title);
        };
        scope.$on('$stateChangeStart', listener);
    }
}

export function directiveFactory($compile) {
    return new PageTitle($compile);
}

directiveFactory.injections = ['$compile', '$timeout'];

export default PageTitle;

0

Forse puoi provare questa direttiva.

https://github.com/afeiship/angular-dynamic-title

Ecco l'esempio:

html:

<title dynamic-title>Title</title>

<a href="javascript:;" ui-sref="state1">State1 page</a>
<a href="javascript:;" ui-sref="state2">State2 page</a>

javascript:

var TestModule = angular.module('TestApp', ['ui.router','nx.widget'])
    .config(function ($stateProvider, $urlRouterProvider) {
      //
      // For any unmatched url, redirect to /state1
      $urlRouterProvider.otherwise("/state1");
      //
      // Now set up the states
      $stateProvider
        .state('state1', {
          url: "/state1",
          templateUrl: "partials/state1.html",
          data:{
            pageTitle:'State1 page title11111'
          }
        })
        .state('state2', {
          url: "/state2",
          templateUrl: "partials/state2.html",data:{
            pageTitle:'State2 page title222222'
          }
        });
    })
    .controller('MainCtrl', function ($scope) {
      console.log('initial ctrl!');
    });

0

Per le versioni aggiornate del router UI 1.0.0+, ( https://ui-router.github.io/guide/ng1/migrate-to-1_0 )

Fare riferimento al codice seguente

app.directive('pageTitle', [
    '$rootScope',
    '$timeout',
    '$transitions',
    function($rootScope, $timeout,$transitions) {
        return {
            restrict: 'A',
            link: function() {
                var listener = function($transitions) {
                    var default_title = "DEFAULT_TITLE";
                    $timeout(function() {
                        	$rootScope.page_title = ($transitions.$to().data && $transitions.$to().data.pageTitle)
                            ? default_title + ' - ' + $transitions.$to().data.pageTitle : default_title;
                    	
                        
                    });
                };
                $transitions.onSuccess({ }, listener);
            }
        }
    }
])

Aggiungi quanto segue al tuo index.html:

<title page-title ng-bind="page_title"></title>

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.