ตั้งชื่อเพจโดยใช้ UI-Router


101

ฉันกำลังย้ายแอพที่ใช้ AngularJS ของฉันไปใช้ ui-router แทนการกำหนดเส้นทางในตัว ฉันได้กำหนดค่าตามที่แสดงด้านล่าง

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

ฉันจะใช้ตัวแปร pageTitle เพื่อตั้งชื่อเพจแบบไดนามิกได้อย่างไร ฉันสามารถใช้การกำหนดเส้นทางในตัวได้

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

จากนั้นผูกตัวแปรใน HTML ดังที่แสดงด้านล่าง

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

มีเหตุการณ์ที่คล้ายกันที่ฉันสามารถเชื่อมต่อโดยใช้ ui-router ได้หรือไม่? ฉันสังเกตว่ามีฟังก์ชัน 'onEnter' และ 'onExit' แต่ดูเหมือนว่าจะเชื่อมโยงกับแต่ละสถานะและจะต้องให้ฉันทำรหัสซ้ำเพื่อตั้งค่าตัวแปร $ rootScope สำหรับแต่ละรัฐ


3
มีเหตุการณ์ $ stateChangeSuccess
เจอราด

คำตอบ:


108

ใช้$stateChangeSuccess.

คุณสามารถวางไว้ในคำสั่ง:

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

และ:

<title update-title></title>

การสาธิต: http://run.plnkr.co/8tqvzlCw62Tl7t4j/#/home

รหัส: http://plnkr.co/edit/XO6RyBPURQFPodoFdYgX?p=preview

ถึงแม้จะมีได้รับสิ่งที่จำเป็นสำหรับประวัติความเป็นมาที่ถูกต้องอย่างน้อยเมื่อผมได้ทดสอบตัวเอง$stateChangeSuccess$timeout


แก้ไข: 24 พ.ย. 2557 - แนวทางการประกาศ:

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

และ:

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

การสาธิต: http://run.plnkr.co/d4s3qBikieq8egX7/#/credits

รหัส: http://plnkr.co/edit/NpzQsxYGofswWQUBGthR?p=preview


สุดยอดมาก สิ่งนี้ไม่สามารถทำได้ง่ายกว่านี้
Matthew Harwood

3
ตัวอย่างนี้ใช้ไม่ได้กับประวัติ (อย่างน้อยใน Chrome 37) หากคุณไปมาระหว่างสถานะต่างๆให้ดูประวัติของคุณชื่อรายการประวัติจะเป็นค่าของหน้าก่อนหน้า ถ้าคุณไปที่ page1 -> page2 -> page3 จากนั้นดูที่ history url ของ page2 จะตรงกับชื่อของ page1
jkjustjoshing

2
อันที่จริงมันไม่ถูกต้องนัก ชื่อหน้าจะเปลี่ยนไปก่อนที่แฮช URL จะเปลี่ยนไปดังนั้นเบราว์เซอร์จึงคิดว่าชื่อใหม่สำหรับเพจเก่า ประวัติปุ่มย้อนกลับจะปิด 1 หน้า การตัดelement.text(title)สายใน $ timeout ใช้ได้ผลสำหรับฉัน การแก้ไขโพสต์ต้นฉบับ
jkjustjoshing

3
วิธีนี้จะใช้ไม่ได้หากชื่อเรื่องต้องเป็นแบบไดนามิกตามพารามิเตอร์ URL บางตัว
Kushagra Gour

10
@KushagraGour หากความต้องการที่ชื่อเพื่อเป็นแบบไดนามิกบนพื้นฐานของ $ stateParams คุณสามารถใช้ฟังก์ชั่นในresolveการสร้างมันแล้วเข้าถึง "การแก้ไข" ค่าในช่วง $ เหตุการณ์ stateChangeSuccess $state.$current.locals.resolve.$$values.NAME_OF_RESOLVE_FUNCTIONด้วย:
Claus Conrad

91

มีอีกวิธีหนึ่งในการทำเช่นนี้โดยรวมคำตอบส่วนใหญ่ไว้ที่นี่แล้ว ฉันรู้ว่ามีคำตอบแล้ว แต่ฉันต้องการแสดงวิธีที่ฉันเปลี่ยนชื่อเพจแบบไดนามิกด้วย ui-router

หากคุณดูแอปตัวอย่าง ui-router พวกเขาใช้บล็อก. runเชิงมุมเพื่อเพิ่มตัวแปร $ state ให้กับ $ 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;
}])

ด้วยการกำหนดนี้คุณสามารถอัปเดตชื่อเพจของคุณแบบไดนามิกด้วยสิ่งที่คุณโพสต์ แต่แก้ไขเพื่อใช้สถานะที่กำหนด:

ตั้งค่าสถานะในลักษณะเดียวกัน:

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

แต่แก้ไข html หน่อย ...

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

ฉันไม่สามารถพูดได้ว่าสิ่งนี้ดีไปกว่าคำตอบก่อนหน้านี้ ... แต่ฉันเข้าใจและนำไปใช้ได้ง่ายกว่า หวังว่านี่จะช่วยใครสักคน!


3
เปิดเผยมากกว่าคำตอบที่ยอมรับ ฉันชอบสิ่งนี้!
Endy Tjahjono

3
ไม่แน่ใจว่าฉันต้องการวัตถุ $ ขอบเขตทั้งหมดใน $ rootScope เฉพาะสำหรับชื่อหน้า ...
Jesús Carrera

ฉันไม่แน่ใจว่าวัตถุ $ ขอบเขตถูกอ้างถึงที่ไหน @ JesúsCarrera
cwbutler

อัพขอโทษฉันหมายถึงวัตถุสถานะ $
Jesús Carrera

4
เพียงเพื่อยืนยันgithub.com/angular-ui/ui-router/wiki/Quick-Referenceยังแนะนำการตั้งค่า$stateและ$stateParamsในจากภายใน$rootScope run
Mark Peterson

17

เชิงมุม-UI เตอร์ชื่อปลั๊กอินทำให้มันง่ายต่อการปรับปรุงชื่อหน้าไปยังคงที่หรือแบบไดนามิกค่าขึ้นอยู่กับสถานะปัจจุบัน มันทำงานได้อย่างถูกต้องกับประวัติเบราว์เซอร์ด้วย


นี่ดูเหมือนจะเป็นทางออกที่ดีที่สุดในอนาคต ฉันสังเกตเห็นความไม่สอดคล้องกันหลายอย่างกับประวัติเบราว์เซอร์โดยใช้วิธีแก้ไขปัญหาอื่น ๆ ในหน้านี้

angular-ui-router-title ดูเหมือนจะเป็นทางออกที่ดีที่สุด ที่สำคัญที่สุดคือไม่ยุ่งยาก! ขอบคุณ Stepan
Dário

มันเป็นไฟล์ต้นฉบับที่เล็กมาก
Tyler Collier

15

$stateChangeSuccessตอนนี้เลิกใช้แล้วใน UI-Router 1.xและปิดใช้งานโดยค่าเริ่มต้น ตอนนี้คุณจะต้องใช้$transitionบริการใหม่

วิธีแก้ปัญหาไม่ยากเกินไปเมื่อคุณเข้าใจวิธีการ$transitionทำงาน ฉันได้รับความช่วยเหลือจาก @troig ในการทำความเข้าใจทั้งหมด นี่คือสิ่งที่ฉันคิดขึ้นเพื่ออัปเดตชื่อเรื่อง

ใส่สิ่งนี้ในแอปพลิเคชัน Angular 1.6 ของคุณ โปรดทราบว่าฉันใช้ไวยากรณ์ ECMAScript 6 ถ้าคุณไม่ได้คุณจะต้องเช่นมีการเปลี่ยนแปลงไปletvar

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

จากนั้นเพิ่มtitleสตริงในสถานะของคุณ:

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

นั่นจะทำให้คำว่า "เพจฟู" ปรากฏขึ้นในหัวเรื่อง (หากสถานะไม่มีชื่อเรื่องชื่อหน้าจะไม่ได้รับการอัปเดตการอัปเดตโค้ดด้านบนเพื่อให้เป็นชื่อเริ่มต้นเป็นเรื่องง่ายหากสถานะไม่ได้ระบุไว้)

titleรหัสนี้ยังช่วยให้คุณสามารถใช้ฟังก์ชั่นสำหรับ สิ่งที่thisใช้ในการเรียกฟังก์ชันจะเป็นสถานะเองและอาร์กิวเมนต์เดียวจะเป็นพารามิเตอร์สถานะดังตัวอย่างนี้:

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

สำหรับเส้นทาง URL /bar/code/123ที่จะแสดง "บาร์โค้ด 123" เป็นชื่อหน้า โปรดทราบว่าฉันใช้ ECMAScript 6 params.codeไวยากรณ์การจัดรูปแบบสตริงและสารสกัดจาก

คงจะดีไม่น้อยถ้าคนที่มีเวลาจะใส่อะไรทำนองนี้เป็นคำสั่งและเผยแพร่ให้ทุกคนได้ใช้


ใช้dataวัตถุสำหรับคีย์ที่กำหนดเอง titleไม่มีอยู่บนStateDeclarationอินเทอร์เฟซ
Gaui

5

การแนบ $ state เข้ากับ $ rootscope เพื่อใช้ที่ใดก็ได้ในแอป

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
รวมสิ่งนี้กับการเพิ่มหัวเรื่องให้กับแต่ละรัฐ ทำงานได้อย่างสมบูรณ์แบบ
WCByrne


5

เพียงอัปเดต window.document.title:

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

ด้วยวิธีนี้ 'ng-app' ไม่จำเป็นต้องเลื่อนขึ้นไปที่แท็ก HTML และสามารถอยู่บนเนื้อความหรือต่ำกว่าได้


1
ทำไมคำตอบนี้ไม่ดีที่สุด? = /
rex

3

ฉันใช้ngMetaซึ่งใช้งานได้ดีสำหรับไม่เพียง แต่ตั้งชื่อหน้า แต่ยังมีคำอธิบายด้วย ช่วยให้คุณสามารถตั้งชื่อเรื่อง / คำอธิบายเฉพาะสำหรับแต่ละรัฐค่าเริ่มต้นเมื่อไม่ได้ระบุชื่อ / คำอธิบายตลอดจนคำต่อท้ายชื่อเริ่มต้น (เช่น '| MySiteName') และค่าผู้แต่ง

$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

คุณสนิทกับคำตอบ / คำถามแรกของคุณจริงๆ เพิ่มชื่อของคุณเป็นวัตถุข้อมูล:

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

ใน index.html ของคุณผูกข้อมูลกับชื่อหน้าโดยตรง:

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

1

ฉันลงเอยด้วยการรวมกันของคำตอบของ Martin และ tasseKATT - ง่ายและไม่มีสิ่งที่เกี่ยวข้องกับเทมเพลต:

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

หากไม่มีสิ่งที่เกี่ยวข้องกับเทมเพลตใด ๆ นักพัฒนาใหม่จะรู้ได้อย่างไรว่ามีการเปลี่ยนชื่อเรื่องโดยไม่ถามว่ามีการเปลี่ยนแปลงอย่างไร
ton.yeung

ถ้าคุณใช้ $ window.document.title $ timeout ก็ไม่มีประโยชน์ ฉันกำลังติดตามแฮ็คนี้เพื่อกำจัด $ timeout และ $ Digest cycle :)
Whisher

1

ทำไมไม่เพียง:

$window.document.title = 'Title';

อัปเดต: รหัส Directive ฉบับเต็ม

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

จากนั้นในทุกหน้าคุณจะรวมคำสั่งนี้ไว้:

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

อาจเป็นเรื่องยากมากที่จะหาสาเหตุ / วิธีการเปลี่ยนชื่อสำหรับทุกคนที่สืบทอดฐานรหัส
ton.yeung

ทำไมมันยากที่จะหา? การสนิปนี้ควรได้รับการทริกเกอร์จากคำสั่งเช่น your-page-titile = "{{'pageTitle' | translate}} คำสั่งนี้จะรวมอยู่ในองค์ประกอบแรกของทุกหน้าคำสั่งที่ดีและชัดเจน
มาร์ติน

โอ้ด้วยการแก้ไขฉันเข้าใจแล้วว่าตอนนี้คุณหมายถึงอะไร สิ่งที่ฉันหมายถึงก็คือซับเส้นเดียวสามารถใส่ได้ทุกที่ $ rootscope คำสั่ง ฯลฯ ...
ton.yeung

0

หากคุณใช้ ES6 สิ่งนี้ก็ใช้ได้ดี :)

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

บางทีคุณอาจลองใช้คำสั่งนี้

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

นี่คือตัวอย่าง:

html:

<title dynamic-title>Title</title>

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

จาวาสคริปต์:

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

สำหรับเวอร์ชันอัปเดต UI-Router 1.0.0+ ( https://ui-router.github.io/guide/ng1/migrate-to-1_0 )

อ้างถึงรหัสต่อไปนี้

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

เพิ่มสิ่งต่อไปนี้ใน index.html ของคุณ:

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.