Stellen Sie den Seitentitel mit dem UI-Router ein


101

Ich migriere meine AngularJS-basierte App, um den UI-Router anstelle des integrierten Routings zu verwenden. Ich habe es wie unten gezeigt konfiguriert

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

Wie kann ich die pageTitle-Variable verwenden, um den Titel der Seite dynamisch festzulegen? Mit dem eingebauten Routing könnte ich tun

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

und binden Sie dann die Variable in HTML wie unten gezeigt

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

Gibt es ein ähnliches Ereignis, an das ich mich mit dem UI-Router anschließen kann? Ich habe festgestellt, dass es die Funktionen 'onEnter' und 'onExit' gibt, aber sie scheinen an jeden Status gebunden zu sein und erfordern, dass ich den Code wiederhole, um die Variable $ rootScope für jeden Status festzulegen.


3
Es gibt ein $ stateChangeSuccess-Ereignis.
Jerrad

Antworten:


108

Verwenden Sie $stateChangeSuccess.

Sie können es in eine Direktive setzen:

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

Und:

<title update-title></title>

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

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

Auch bei $stateChangeSuccessder $timeouterforderlich ist für die Geschichte richtig zu sein, zumindest wenn ich habe mich getestet.


Bearbeiten: 24. November 2014 - Deklarativer Ansatz:

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

Und:

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

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

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


Super toll. Einfacher geht es nicht
Matthew Harwood

3
Dieses Beispiel funktioniert auch nicht ordnungsgemäß mit dem Verlauf (zumindest in Chrome 37). Wenn Sie zwischen verschiedenen Status wechseln und sich dann Ihren Verlauf ansehen, entspricht der Titel des Verlaufselements dem Wert der vorherigen Seite. Wenn Sie zu Seite1 -> Seite2 -> Seite3 gehen und dann den Verlauf anzeigen, wird die URL von Seite2 mit dem Titel von Seite1 abgeglichen.
jkjustjoshing

2
Eigentlich ist das nicht ganz genau. Der Seitentitel ändert sich, bevor sich der URL-Hash ändert, sodass der Browser den neuen Titel für die alte Seite hält. Der Verlauf der Zurück-Schaltfläche ist dann 1 Seite entfernt. Das Einschließen des element.text(title)Anrufs in eine $ -Zeitüberschreitung hat für mich funktioniert. Bearbeiten des ursprünglichen Beitrags.
jkjustjoshing

3
Dies funktioniert nicht, wenn der Titel basierend auf einigen URL-Parametern dynamisch sein muss.
Kushagra Gour

10
@KushagraGour Wenn der Titel basierend auf den $ stateParams dynamisch sein muss, können Sie eine Funktion in verwenden resolve, um ihn zu generieren, und dann während des $ stateChangeSuccess-Ereignisses auf den "aufgelösten" Wert zugreifen mit : $state.$current.locals.resolve.$$values.NAME_OF_RESOLVE_FUNCTION.
Claus Conrad

91

Es gibt eine andere Möglichkeit, dies zu tun, indem die meisten Antworten hier bereits kombiniert werden. Ich weiß, dass dies bereits beantwortet wurde, aber ich wollte zeigen, wie ich Seitentitel mit dem UI-Router dynamisch ändere.

Wenn Sie sich die Beispiel-App von ui-router ansehen , verwenden sie den eckigen .run- Block, um die $ state-Variable zu $ ​​rootScope hinzuzufügen.

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

Wenn dies definiert ist, können Sie Ihren Seitentitel einfach dynamisch mit dem aktualisieren, was Sie gepostet, aber geändert haben, um den definierten Status zu verwenden:

Richten Sie den Status auf die gleiche Weise ein:

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

Aber bearbeite das HTML ein bisschen ...

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

Ich kann nicht sagen, dass dies besser ist als die Antworten zuvor ... aber es war für mich einfacher zu verstehen und umzusetzen. Hoffe das hilft jemandem!


3
Deklarativer als die akzeptierte Antwort. Ich mag das!
Endy Tjahjono

3
Ich bin mir nicht sicher, ob ich das gesamte $ scope-Objekt in $ rootScope nur für den Seitentitel haben möchte ...
Jesús Carrera

Ich bin nicht sicher, wo auf das $ scope-Objekt verwiesen wird @ JesúsCarrera
cwbutler

Ups, sorry ich meine das $ state Objekt
Jesús Carrera

4
nur um zu bestätigen github.com/angular-ui/ui-router/wiki/Quick-Reference auch Einstellung empfiehlt $stateund $stateParamsauf $rootScopevon innen run.
Mark Peterson

17

Das Angular-UI-Router-Titel- Plugin erleichtert das Aktualisieren des Seitentitels auf einen statischen oder dynamischen Wert basierend auf dem aktuellen Status. Es funktioniert auch korrekt mit dem Browserverlauf.


Dies scheint die beste Lösung für die Zukunft zu sein. Ich habe einige Inkonsistenzen mit dem Browserverlauf bei einigen der anderen Lösungen auf dieser Seite festgestellt.

Angular-UI-Router-Titel scheint die beste Lösung zu sein. Vor allem ist es problemlos! Danke Stepan.
Dário

Es ist eine sehr kleine Quelldatei.
Tyler Collier

15

$stateChangeSuccessist jetzt in UI-Router 1.x veraltet und standardmäßig deaktiviert. Sie müssen jetzt den neuen $transitionDienst verwenden.

Eine Lösung ist nicht allzu schwierig, wenn Sie erst einmal verstanden haben, wie es $transitionfunktioniert. Ich habe Hilfe von @troig bekommen, um alles zu verstehen. Folgendes habe ich mir für die Aktualisierung des Titels ausgedacht.

Fügen Sie dies in Ihre Angular 1.6-Anwendung ein. Beachten Sie, dass ich die ECMAScript 6-Syntax verwende. wenn Sie es nicht sind, müssen Sie zB Änderungen letzu 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;
        }
    });

Fügen Sie dann einfach eine titleZeichenfolge zu Ihrem Status hinzu:

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

Dadurch werden die Wörter "Foo Page" im Titel angezeigt. (Wenn ein Status keinen Titel hat, wird der Seitentitel nicht aktualisiert. Es wäre einfach, den obigen Code zu aktualisieren, um einen Standardtitel bereitzustellen, wenn ein Status keinen angibt.)

Mit dem Code können Sie auch eine Funktion für verwenden title. Der thiszum Aufrufen der Funktion verwendete Wert ist der Status selbst, und das einzige Argument sind die Statusparameter, wie in diesem Beispiel:

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

Für den URL-Pfad /bar/code/123, in dem "Barcode 123" als Seitentitel angezeigt wird. Beachten Sie, dass ich die ECMAScript 6-Syntax verwende, um die Zeichenfolge zu formatieren und zu extrahieren params.code.

Es wäre schön, wenn jemand, der Zeit hätte, so etwas in eine Richtlinie aufnehmen und für alle veröffentlichen würde.


Verwenden Sie dataObjekt für benutzerdefinierte Schlüssel. titleexistiert nicht auf der StateDeclarationSchnittstelle.
Gaui

5

Anhängen von $ state an $ rootcope zur Verwendung an einer beliebigen Stelle in der 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
Kombiniert mit dem Hinzufügen von Titeln zu jedem Status. Funktioniert perfekt.
WCByrne

5

Ich fand diesen Weg wirklich einfach:

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

und dann in meinem HTML so:

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

5

Aktualisieren Sie einfach window.document.title:

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

Auf diese Weise muss 'ng-app' nicht zum HTML-Tag aufsteigen und kann auf dem Körper oder darunter bleiben.


1
Warum ist das nicht die beste Antwort? = /
Rex

3

Ich verwende ngMeta , das sich nicht nur zum Festlegen des Seitentitels , sondern auch zum Beschreiben von Beschreibungen eignet . Hier können Sie einen bestimmten Titel / eine bestimmte Beschreibung für jeden Status festlegen, standardmäßig, wenn kein Titel / eine Beschreibung angegeben ist, sowie Standardtitelsuffixe (dh '| MySiteName') und einen Autorenwert.

$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

Sie sind mit Ihrer ersten Antwort / Frage wirklich sehr nah dran. Fügen Sie Ihren Titel als Datenobjekt hinzu:

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

Binden Sie in Ihrer index.html die Daten direkt an den Seitentitel:

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

1

Am Ende hatte ich diese Kombination aus Martins und tasseKATTs Antworten - einfach und ohne vorlagenbezogene Dinge:

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

Wenn es keine Vorlagen gibt, wie würde ein neuer Entwickler wissen, wie der Titel geändert wurde, ohne zu fragen, wie er geändert wurde?
ton.yeung

Wenn Sie $ window.document.title $ verwenden, ist das Timeout nutzlos. Ich verfolge diesen Hackish, nur um $ Timeout und einen $ Digest-Zyklus loszuwerden :)
Whisher

1

Warum nicht einfach:

$window.document.title = 'Title';

UPDATE: Vollständiger Richtliniencode

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

Dann würden Sie auf jeder Seite nur diese Anweisung einfügen:

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

Es könnte möglicherweise sehr schwierig sein herauszufinden, warum / wie sich der Titel für jemanden ändert, der die Codebasis erbt
ton.yeung

Warum sollte das schwer herauszufinden sein? Dieser Ausschnitt sollte von einer Direktive ausgelöst werden, sagen Sie your-page-titile = "{{'pageTitle' | translate}}. Diese Direktive würde in das erste Element jeder Seite aufgenommen. Schön und deklarativ klar.
Martin

Oh, mit der Bearbeitung sehe ich, was du jetzt meinst. Was ich meinte war, dass der eine Liner möglicherweise irgendwo platziert werden könnte, $ Rootscope, Direktive, etc ...
ton.yeung

0

Wenn Sie ES6 verwenden, funktioniert dies einwandfrei :).

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

Vielleicht können Sie diese Richtlinie ausprobieren.

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

Hier ist das Beispiel:

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

Für aktualisierte UI-Router 1.0.0+ Versionen ( https://ui-router.github.io/guide/ng1/migrate-to-1_0 )

Siehe folgenden Code

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

Fügen Sie Ihrer index.html Folgendes hinzu:

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.