Đặt tiêu đề trang bằng UI-Router


101

Tôi đang di chuyển ứng dụng dựa trên AngularJS của mình để sử dụng ui-router thay vì định tuyến tích hợp sẵn. Tôi đã cấu hình nó như hình dưới đây

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

Làm cách nào để sử dụng biến pageTitle để đặt động tiêu đề của trang? Sử dụng định tuyến tích hợp, tôi có thể làm

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

và sau đó liên kết biến trong HTML như được hiển thị bên dưới

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

Có một sự kiện tương tự mà tôi có thể tham gia bằng cách sử dụng ui-router không? Tôi nhận thấy rằng có các hàm 'onEnter' và 'onExit' nhưng chúng dường như bị ràng buộc với từng trạng thái và sẽ yêu cầu tôi lặp lại mã để đặt biến $ rootScope cho từng trạng thái.


3
Có một sự kiện $ stateChangeSuccess.
Jerrad

Câu trả lời:


108

Sử dụng $stateChangeSuccess.

Bạn có thể đặt nó trong một chỉ thị:

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

Và:

<title update-title></title>

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

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

Ngay cả với $stateChangeSuccessnhững điều $timeoutcần thiết để lịch sử chính xác, ít nhất là khi tôi đã tự kiểm tra.


Chỉnh sửa: Ngày 24 tháng 11 năm 2014 - Cách tiếp cận so sánh:

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

Và:

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

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

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


Siêu tuyệt vời. Điều này không thể dễ dàng hơn
Matthew Harwood

3
Ví dụ này cũng không hoạt động đúng với lịch sử (ít nhất là trong Chrome 37). Nếu bạn đi giữa các trạng thái khác nhau, sau đó xem lịch sử của bạn, tiêu đề của mục lịch sử sẽ là giá trị của trang trước đó. Nếu bạn truy cập trang1 -> trang2 -> trang3, sau đó xem lịch sử, url của trang2 sẽ được khớp với tiêu đề của trang1.
jkjustjoshing

2
Trên thực tế, điều đó không chính xác lắm. Tiêu đề trang thay đổi trước khi băm URL thay đổi, vì vậy trình duyệt nghĩ rằng tiêu đề mới dành cho trang cũ. Lịch sử nút quay lại sau đó bị tắt 1 trang. Kết thúc element.text(title)cuộc gọi trong thời gian chờ $ đã hiệu quả với tôi. Chỉnh sửa bài gốc.
jkjustjoshing

3
Điều này sẽ không hoạt động nếu tiêu đề cần phải động dựa trên một số tham số url.
Kushagra Gour

10
@KushagraGour Nếu nhu cầu tiêu đề phải năng động dựa trên $ stateParams, bạn có thể sử dụng một hàm trong resolveđể tạo ra nó, sau đó truy cập vào "giải quyết" giá trị trong $ stateChangeSuccess kiện với: $state.$current.locals.resolve.$$values.NAME_OF_RESOLVE_FUNCTION.
Claus Conrad

91

Có một cách khác để làm điều này bằng cách kết hợp hầu hết các câu trả lời ở đây. Tôi biết điều này đã được trả lời nhưng tôi muốn hiển thị cách tôi thay đổi động tiêu đề trang bằng ui-router.

Nếu bạn xem ứng dụng mẫu ui-router , họ sử dụng khối .run góc cạnh để thêm biến $ state vào $ 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;
}])

Với định nghĩa này, bạn có thể dễ dàng cập nhật động tiêu đề trang của mình với những gì bạn đã đăng nhưng được sửa đổi để sử dụng trạng thái đã xác định:

Thiết lập trạng thái theo cùng một cách:

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

Nhưng chỉnh sửa html một chút ...

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

Tôi không thể nói điều này tốt hơn bất kỳ câu trả lời nào trước đây ... nhưng nó dễ hiểu và dễ thực hiện hơn đối với tôi. Hy vọng điều này sẽ giúp ai đó!


3
Khai báo nhiều hơn câu trả lời được chấp nhận. Tôi thích điều này!
Endy Tjahjono

3
Bạn không chắc chắn nếu tôi muốn các đối tượng toàn bộ $ phạm vi trong $ rootScope chỉ dành riêng cho các tiêu đề trang ...
Jesús Carrera

Tôi không chắc chắn nơi các đối tượng $ phạm vi đang được tham chiếu @ JesúsCarrera
cwbutler

Ups, xin lỗi tôi có nghĩa là các đối tượng $ nhà nước
Jesús Carrera

4
chỉ để xác nhận github.com/angular-ui/ui-router/wiki/Quick-Reference cũng đề xuất cài đặt $state$stateParamsbật $rootScopetừ bên trong run.
Mark Peterson

17

Các góc-ui-router-tiêu đề Plugin giúp bạn dễ dàng cập nhật các tiêu đề trang để một tĩnh hoặc động giá trị dựa trên trạng thái hiện tại. Nó cũng hoạt động chính xác với lịch sử trình duyệt.


Đây dường như là giải pháp tốt nhất trong tương lai. Tôi đã nhận thấy một số điểm mâu thuẫn với lịch sử trình duyệt bằng cách sử dụng một số giải pháp khác trên trang này.

angle-ui-router-title dường như là giải pháp tốt nhất. Hơn hết nó không có rắc rối phức tạp! Cảm ơn Stepan.
Dário

Đó là một tệp nguồn rất nhỏ.
Tyler Collier

15

$stateChangeSuccesshiện không được chấp nhận trong UI-Router 1.x và bị tắt theo mặc định. Bây giờ bạn sẽ cần sử dụng $transitiondịch vụ mới .

Một giải pháp không quá khó khi bạn hiểu cách $transitionhoạt động. Tôi đã nhận được một số trợ giúp từ @troig để hiểu tất cả. Đây là những gì tôi nghĩ ra để cập nhật tiêu đề.

Đặt cái này vào ứng dụng Angular 1.6 của bạn. Lưu ý rằng tôi đang sử dụng cú pháp ECMAScript 6; nếu không, bạn sẽ cần ví dụ: thay đổi letthành 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;
        }
    });

Sau đó, chỉ cần thêm một titlechuỗi vào trạng thái của bạn:

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

Điều đó sẽ làm cho các từ "Foo Page" hiển thị trong tiêu đề. (Nếu trạng thái không có tiêu đề, tiêu đề trang sẽ không được cập nhật. Sẽ rất đơn giản nếu cập nhật mã ở trên để cung cấp tiêu đề mặc định nếu trạng thái không chỉ ra tiêu đề đó.)

Mã này cũng cho phép bạn sử dụng một hàm cho title. Hàm thisđược sử dụng để gọi sẽ là chính trạng thái và một đối số sẽ là các tham số trạng thái, như ví dụ sau:

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

Đối với đường dẫn URL /bar/code/123sẽ hiển thị "Mã vạch 123" làm tiêu đề trang. Lưu ý rằng tôi đang sử dụng cú pháp ECMAScript 6 để định dạng chuỗi và trích xuất params.code.

Sẽ rất tuyệt nếu ai đó có thời gian đưa một cái gì đó như thế này vào một chỉ thị và xuất bản nó cho mọi người sử dụng.


Sử dụng datađối tượng cho các phím tùy chỉnh. titlekhông tồn tại trên StateDeclarationgiao diện.
Gaui

5

Đính kèm $ state với $ rootcope để sử dụng ở mọi nơi trong ứng dụng.

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
Kết hợp điều này với việc thêm tiêu đề cho mỗi tiểu bang. Hoạt động hoàn hảo.
WCByrne

5

Tôi thấy cách này thực sự dễ dàng:

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

và sau đó trong HTML của tôi như thế này:

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

5

Chỉ cần cập nhật window.document.title:

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

Bằng cách đó, 'ng-app' không cần chuyển đến thẻ HTML và có thể ở trên phần thân hoặc thấp hơn.


1
Tại sao đây không phải là câu trả lời tốt nhất? = /
rex

3

Tôi đang sử dụng ngMeta , hoạt động tốt không chỉ để đặt tiêu đề trang mà còn cả mô tả. Nó cho phép bạn đặt tiêu đề / mô tả cụ thể cho từng trạng thái, mặc định khi tiêu đề / mô tả không được chỉ định, cũng như các hậu tố tiêu đề mặc định (ví dụ: '| MySiteName') và giá trị tác giả.

$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

Bạn thực sự gần với câu trả lời / câu hỏi đầu tiên của mình. Thêm tiêu đề của bạn dưới dạng đối tượng dữ liệu:

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

Trong index.html của bạn, liên kết dữ liệu trực tiếp với tiêu đề trang:

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

1

Tôi đã kết thúc với sự kết hợp câu trả lời của Martin và tasseKATT - đơn giản và không có bất kỳ nội dung liên quan đến mẫu nào:

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

nếu không có bất kỳ nội dung nào liên quan đến mẫu, làm thế nào một nhà phát triển mới có thể biết tiêu đề đã được thay đổi như thế nào mà không hỏi nó đã được thay đổi như thế nào?
ton.yeung

nếu bạn sử dụng $ window.document.title $ timeout là vô ích. Tôi đang theo dõi hackish này chỉ để thoát khỏi $ timeout và một chu kỳ tiêu hóa $ :)
Whisher

1

Tại sao không chỉ:

$window.document.title = 'Title';

CẬP NHẬT: Mã chỉ thị đầy đủ

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

Sau đó, trong mỗi trang, bạn chỉ cần bao gồm chỉ thị này:

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

Có thể rất khó để tìm ra lý do tại sao / tiêu đề thay đổi như thế nào đối với bất kỳ ai kế thừa cơ sở mã
ton.yeung

Tại sao điều đó lại khó tìm ra? Đoạn mã này phải được kích hoạt từ một chỉ thị, chẳng hạn như your-page-titile = "{{'pageTitle' | translate}}. Chỉ thị này sẽ được bao gồm trong phần tử đầu tiên của mỗi trang. Tốt và rõ ràng.
Martin

ồ, với bản chỉnh sửa, tôi hiểu ý bạn bây giờ. ý tôi là một lớp lót có khả năng được đặt ở bất cứ đâu, $ rootcope, chỉ thị, v.v.
ton.yeung

0

Nếu bạn đang sử dụng ES6, điều này hoạt động tốt :).

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

Có lẽ bạn có thể thử chỉ thị này.

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

Đây là ví dụ:

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

Đối với các phiên bản UI-Router 1.0.0+ được cập nhật, ( https://ui-router.github.io/guide/ng1/migrate-to-1_0 )

Tham khảo mã sau

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

Thêm phần sau vào index.html của bạn:

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.