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.


quelle
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

tasseKATT
quelle
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!

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

Stepan Riha
quelle
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.

Garret Wilson
quelle
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>

simbu
quelle
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>
Mike Rouse
quelle
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.

getetbro
quelle
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.'
    },
  })
Drichar
quelle
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>
Tristan
quelle
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; 
   });
});
rauben
quelle
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}}">
Martin
quelle
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;
TGarrett
quelle
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!');
    });
Fei Zheng
quelle
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>

Palsri
quelle