Ustaw tytuł strony za pomocą Routera UI

101

Migrację mojej aplikacji opartej na AngularJS używam routera ui zamiast wbudowanego routingu. Mam to skonfigurowane, jak pokazano poniżej

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

Jak mogę użyć zmiennej pageTitle, aby dynamicznie ustawić tytuł strony? Mogłem to zrobić, korzystając z wbudowanego routingu

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

a następnie powiąż zmienną w HTML, jak pokazano poniżej

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

Czy istnieje podobne zdarzenie, do którego mogę podłączyć się za pomocą routera ui? Zauważyłem, że istnieją funkcje „onEnter” i „onExit”, ale wydają się one być powiązane z każdym stanem i będą wymagać ode mnie powtórzenia kodu w celu ustawienia zmiennej $ rootScope dla każdego stanu.


źródło
3
Istnieje zdarzenie $ stateChangeSuccess.
Jerrad

Odpowiedzi:

108

Użyj $stateChangeSuccess.

Możesz umieścić to w dyrektywie:

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

I:

<title update-title></title>

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

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

Nawet został potrzebna historia się być poprawne, przynajmniej jeśli Przetestowałem siebie.$stateChangeSuccess$timeout


Edycja: 24 listopada 2014 r. - Podejście deklaratywne:

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

I:

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

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

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

tasseKATT
źródło
Super świetnie. To nie mogło być łatwiejsze
Matthew Harwood
3
Ten przykład również nie działa poprawnie z historią (przynajmniej w Chrome 37). Jeśli przechodzisz między różnymi stanami, a następnie spójrz na historię, tytuł elementu historii będzie wartością poprzedniej strony. Jeśli przejdziesz page1 -> page2 -> page3, a następnie spójrz na historię, adres strony2 zostanie dopasowany do tytułu strony1.
jkjustjoshing
2
Właściwie to nie jest całkiem dokładne. Tytuł strony zmienia się przed zmianą skrótu adresu URL, więc przeglądarka uważa, że ​​nowy tytuł dotyczy starej strony. Historia przycisku Wstecz jest wtedy 1 strona wyłączona. Zawijanie element.text(title)połączenia w $ timeout działało dla mnie. Edycja oryginalnego posta.
jkjustjoshing
3
To nie zadziała, jeśli tytuł musi być dynamiczny w oparciu o niektóre parametry adresu URL.
Kushagra Gour
10
@KushagraGour razie potrzeby tytułowe być dynamiczny w oparciu o $ stateParams, można użyć funkcji w resolvecelu wygenerowania go, a następnie przejść do „rozwiązany” wartość podczas $ wydarzenie stateChangeSuccess z: $state.$current.locals.resolve.$$values.NAME_OF_RESOLVE_FUNCTION.
Claus Conrad
91

Można to zrobić w inny sposób, łącząc już większość odpowiedzi. Wiem, że już na to odpowiedziano, ale chciałem pokazać sposób, w jaki dynamicznie zmieniam tytuły stron za pomocą routera ui.

Jeśli spojrzysz na przykładową aplikację ui-router , użyje ona bloku angular .run , aby dodać zmienną $ state do $ 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;
}])

Dzięki tej definicji możesz łatwo dynamicznie aktualizować tytuł strony o to, co opublikowałeś, ale zmodyfikowałeś, aby używać zdefiniowanego stanu:

Ustaw stan w ten sam sposób:

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

Ale edytuj trochę HTML ...

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

Nie mogę powiedzieć, że jest to lepsze niż wcześniejsze odpowiedzi ... ale było dla mnie łatwiejsze do zrozumienia i wdrożenia. Mam nadzieję, że to komuś pomoże!

cwbutler
źródło
3
Bardziej deklaratywne niż zaakceptowana odpowiedź. Lubię to!
Endy Tjahjono
3
Nie jestem pewien, czy chciałbym cały obiekt $ scope w $ rootScope tylko dla tytułu strony ...
Jesús Carrera
Nie jestem pewien, gdzie znajduje się odniesienie do obiektu $ scope @ JesúsCarrera
cwbutler
Ups, przepraszam, mam na myśli obiekt stanu $
Jesús Carrera
4
aby potwierdzić, github.com/angular-ui/ui-router/wiki/Quick-Reference również zaleca ustawienie $statei $stateParamswłączenie $rootScopeod wewnątrz run.
Mark Peterson,
17

Kątowe-ui-router tytuł plugin ułatwia zaktualizować tytuł strony do statycznej lub dynamicznej wartości w oparciu o bieżący stan. Działa również poprawnie z historią przeglądarki.

Stepan Riha
źródło
Wydaje się, że jest to najlepsze rozwiązanie na przyszłość. Zauważyłem kilka niespójności z historią przeglądarki przy użyciu niektórych innych rozwiązań na tej stronie.
angular-ui-router-title wydaje się być najlepszym rozwiązaniem. Przede wszystkim jest to bezproblemowe! Dzięki, Stepan.
Dário
To bardzo mały plik źródłowy.
Tyler Collier
15

$stateChangeSuccessjest teraz przestarzałe w UI-Router 1.xi domyślnie wyłączone. Musisz teraz skorzystać z nowej $transitionusługi.

Rozwiązanie nie jest zbyt trudne, gdy zrozumiesz, jak $transitiondziała. Mam jakąś pomoc od @troig zrozumieć to wszystko. Oto, co wymyśliłem, aby zaktualizować tytuł.

Umieść to w swojej aplikacji Angular 1.6. Zauważ, że używam składni ECMAScript 6; Jeśli nie są, trzeba na przykład zmienić letsię 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;
        }
    });

Następnie po prostu dodaj titleciąg do swojego stanu:

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

Dzięki temu w tytule pojawią się słowa „Foo Page”. (Jeśli stan nie ma tytułu, tytuł strony nie zostanie zaktualizowany. Łatwo byłoby zaktualizować powyższy kod w celu podania tytułu domyślnego, jeśli stan go nie wskazuje).

Kod umożliwia również użycie funkcji dla title. thisStosowane do wywołania funkcji będzie sam stan, a jeden argument będą parametry stanu, jak to przykładowo:

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

W przypadku ścieżki adresu URL /bar/code/123, która jako tytuł strony wyświetla „Kod kreskowy 123”. Zauważ, że używam składni ECMAScript 6 do formatowania ciągu i wyodrębniania params.code.

Byłoby miło, gdyby ktoś, kto ma czas, umieściłby coś takiego w dyrektywie i opublikowałby ją dla wszystkich.

Garret Wilson
źródło
Użyj dataobiektu dla kluczy niestandardowych. titlenie istnieje w StateDeclarationinterfejsie.
Gaui,
5

Dołączanie $ state do $ rootscope w celu użycia w dowolnym miejscu aplikacji.

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
źródło
1
W połączeniu z dodaniem tytułu do każdego stanu. Działa idealnie.
WCByrne
5

Znalazłem ten sposób bardzo prosty:

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

a potem w moim HTML:

<h3>{{ $state.current.title }}</h3>
Mike Rouse
źródło
5

Po prostu zaktualizuj window.document.title:

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

W ten sposób „ng-app” nie musi przesuwać się w górę do znacznika HTML i może pozostać w treści lub niżej.

getsetbro
źródło
1
Dlaczego nie jest to najlepsza odpowiedź? = /
rex
3

Używam ngMeta , który działa dobrze nie tylko do ustawiania tytułu strony, ale także opisów. Umożliwia ustawienie konkretnego tytułu / opisu dla każdego stanu, wartości domyślnych, gdy tytuł / opis nie jest określony, a także domyślnych sufiksów tytułów (tj. „| MySiteName”) i wartości autora.

$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
źródło
2

Twoja pierwsza odpowiedź / pytanie jest naprawdę blisko. Dodaj swój tytuł jako obiekt danych:

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

W pliku index.html połącz dane bezpośrednio z tytułem strony:

<title data-ng-bind="$state.current.data.pageTitle + ' - Optional text'">Failsafe text</title>
Tristan
źródło
1

Skończyło się na takiej kombinacji odpowiedzi Martina i tasseKATTA - prostej i bez żadnych elementów związanych z szablonem:

$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; 
   });
});
Obrabować
źródło
jeśli nie ma żadnych rzeczy związanych z szablonem, skąd nowy programista miałby wiedzieć, jak zmieniał się tytuł, bez pytania, jak to się robi?
ton.yeung
jeśli używasz $ window.document.title $ timeout jest bezużyteczny. Śledzę ten hackish tylko po to, aby pozbyć się $ timeout i $ digestu :)
Whisher
1

Dlaczego nie tylko:

$window.document.title = 'Title';

AKTUALIZACJA: Pełny kod dyrektywy

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

Następnie na każdej stronie wystarczy umieścić tę dyrektywę:

<section
    your-page-title="{{'somePage' | translate}}">
Jaskółka oknówka
źródło
może być bardzo trudno dowiedzieć się, dlaczego / jak zmienia się tytuł dla każdego, kto odziedziczy podstawę kodu
ton.yeung
Dlaczego miałoby to być trudne do ustalenia? To wycięcie powinno być wywołane przez dyrektywę, powiedzmy your-page-titile = "{{'pageTitle' | translate}}. Ta dyrektywa będzie zawarta w pierwszym elemencie każdej strony. Ładnie i deklaratywnie jasne.
Martin
och, po edycji rozumiem, co masz na myśli. chodziło mi o to, że jedna linijka mogłaby być potencjalnie umieszczona gdziekolwiek, $ rootscope, dyrektywa, itp ...
ton.yeung
0

Jeśli używasz ES6, to działa dobrze :).

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
źródło
0

Może możesz wypróbować tę dyrektywę.

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

Oto przykład:

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
źródło
0

W przypadku zaktualizowanych wersji UI-Router 1.0.0+ ( https://ui-router.github.io/guide/ng1/migrate-to-1_0 )

Zobacz poniższy kod

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

Dodaj następujące informacje do swojego index.html:

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

Palsri
źródło