Angular - ui-router รับสถานะก่อนหน้า


152

มีวิธีรับสถานะก่อนหน้าของสถานะปัจจุบันหรือไม่

ตัวอย่างเช่นฉันต้องการทราบว่าสถานะก่อนหน้านี้คืออะไรก่อนสถานะปัจจุบัน B (โดยที่สถานะก่อนหน้านี้จะเป็นสถานะ A)

ฉันไม่สามารถหามันได้ในหน้า ui-router github doc


คำตอบด้านล่างนี้ถูกต้องคุณสามารถค้นหาข้อมูลทั้งหมดที่คุณต้องการจากแหล่งที่มาหากเอกสารไม่เพียงพอที่จะช่วยgoo.gl/9B9bH
David Chase

คำตอบ:


133

ui-router ไม่ได้ติดตามสถานะก่อนหน้านี้เมื่อมีการเปลี่ยน แต่เหตุการณ์$stateChangeSuccessจะออกอากาศใน$rootScopeเมื่อสถานะเปลี่ยนไป

คุณควรจะสามารถจับสถานะก่อนหน้าจากเหตุการณ์นั้น ( fromคือสถานะที่คุณกำลังจะออก):

$rootScope.$on('$stateChangeSuccess', function (ev, to, toParams, from, fromParams) {
   //assign the "from" parameter to something
});

3
มีตัวอย่างอะไรบ้างที่ใช้ "จาก"
พอล

ที่นี่ 'ถึง' และ 'จาก' หมายถึง 'toState' และ 'fromState' พิจารณา URL ก่อนหน้านี้ของคุณคือ localhost: xxxx / พนักงานและตัวควบคุมคือ 'EmployeesController' จากนั้นตัวอย่างสำหรับ 'fromState' คือ: วัตถุ {url: "/ พนักงาน", templateUrl: "/ พนักงาน", ตัวควบคุม: "EmployeesController" ชื่อ: " พนักงาน "}
Ranadheer Reddy

3
ฉันทำสิ่งนี้ในนามธรรมของฉัน: `$ rootScope.previousState; $ rootScope.currentState; $ rootScope. $ on ('$ stateChangeSuccess', ฟังก์ชั่น (ev, ถึง, toParams, จาก, fromParams) {$ rootScope.previousState = from.name; $ rootScope.currentState = to.name; console.log ('สถานะก่อนหน้า: '+ $ rootScope.previousState) console.log (' สถานะปัจจุบัน: '+ $ rootScope.currentState)}); `ติดตามก่อนหน้าและปัจจุบันใน rootScope มีประโยชน์มาก!
Federico

การใช้โซลูชันของ @ endy-tjahjono ( stackoverflow.com/a/25945003/2837519 ) เป็นแบบอินไลน์ของ ui-router 1.x
ปีเตอร์ Ahlers

ฉันชอบวิธีที่ง่าย ๆ กับ history.back () <a href="javascript:history.back()" class="btn btn-default no-radius">
Serip88

146

ฉันใช้การแก้ไขเพื่อบันทึกข้อมูลสถานะปัจจุบันก่อนที่จะย้ายไปยังสถานะใหม่:

angular.module('MyModule')
.config(['$stateProvider', function ($stateProvider) {
    $stateProvider
        .state('mystate', {
            templateUrl: 'mytemplate.html',
            controller: ["PreviousState", function (PreviousState) {
                if (PreviousState.Name == "mystate") {
                    // ...
                }
            }],
            resolve: {
                PreviousState: ["$state", function ($state) {
                    var currentStateData = {
                        Name: $state.current.name,
                        Params: $state.params,
                        URL: $state.href($state.current.name, $state.params)
                    };
                    return currentStateData;
                }]
            }
        });
}]);

8
ดีกว่าการซื้อขายกับ$stateChangeSuccess
SET

10
ในความคิดของฉันนี้ควรเป็นคำตอบที่ยอมรับ เหตุการณ์แม้ว่าจะใช้$stateChangeSuccessงานได้ แต่ก็ทำในระดับโลกซึ่งไม่จำเป็นต้องใช้เวลาส่วนใหญ่
ปิแอร์สปริง

2
ฉันเห็นด้วย. มันสะอาดกว่ามาก หากคุณกำลังจัดการกับโมดูลจำนวนมากที่ทุกรัฐมีสถานะ $ stateChangeSuccess จะเริ่มต้นสำหรับการเปลี่ยนแปลงสถานะทุกครั้งไม่ใช่เฉพาะในโมดูลของคุณ โหวตอีกครั้งสำหรับเรื่องนี้เป็นทางออกที่ดีกว่า
Jason Buchanan

1
@ Nissassin17 คุณใช้รุ่นไหน ใน v0.2.15 เมื่อเปิดรัฐโดยตรงเป็นวัตถุ:$state.current {name: "", url: "^", views: null, abstract: true}
Endy Tjahjono

3
FYI - ฉันต้องทำสิ่งต่อไปนี้: Params: angular.copy ($ state.params) - คำอธิบาย: ฉันใช้ UI-Router v 1.0.0-beta 2 และฉันมีปัญหากับส่วน Params ของรหัสนี้; เนื่องจาก $ state.params เป็นวัตถุมันจะอัปเดตเป็นมุมมองปัจจุบันหลังจากแก้ไขฟังก์ชั่นแล้ว ... ฉันต้องทำสิ่งนี้ในฟังก์ชั่นแก้ไขเพื่อป้องกันไม่ให้วัตถุถูกอัพเดตในมุมมองปัจจุบัน
Joe H

100

เพื่อประโยชน์ในการอ่านฉันจะวางทางออกของฉัน (ตาม anwser ของ stu.salsbury) ที่นี่

เพิ่มรหัสนี้ลงในเทมเพลตบทคัดย่อของแอปเพื่อให้ทำงานในทุกหน้า

$rootScope.previousState;
$rootScope.currentState;
$rootScope.$on('$stateChangeSuccess', function(ev, to, toParams, from, fromParams) {
    $rootScope.previousState = from.name;
    $rootScope.currentState = to.name;
    console.log('Previous state:'+$rootScope.previousState)
    console.log('Current state:'+$rootScope.currentState)
});

ติดตามการเปลี่ยนแปลงใน rootScope มันค่อนข้างมีประโยชน์


19
มันคุ้มค่าที่จะเก็บ fromParams เช่นกันถ้าคุณต้องการที่จะนำคนกลับไปที่ที่พวกเขามา
Yaron

ฉันชอบสิ่งนี้และนำไปใช้ในแอปพลิเคชันของฉัน ขอบคุณ
Fallenreaper

มีประโยชน์จริงๆ ... ยิ่งใช้ localStorage คุณสามารถรับสถานะก่อนหน้าได้ทุกที่
georgeos

14

ในตัวอย่างต่อไปนี้ฉันสร้างdecorator(วิ่งเพียงครั้งเดียวต่อแอปเฟสการตั้งค่า) และเพิ่มคุณสมบัติพิเศษในการ$stateบริการดังนั้นวิธีการนี้จะไม่เพิ่มระดับโลกตัวแปร$rootscopeและไม่ต้องการที่จะเพิ่มการพึ่งพาพิเศษใด ๆ ในการบริการอื่น ๆ $stateกว่า

ในตัวอย่างของฉันฉันต้องการเปลี่ยนเส้นทางผู้ใช้ไปยังหน้าดัชนีเมื่อเขาลงชื่อเข้าใช้แล้วและเมื่อเขาไม่ได้เปลี่ยนเส้นทางเขาไปยังหน้า "ป้องกัน" ก่อนหน้าหลังจากลงชื่อเข้าใช้

บริการที่ไม่รู้จักเท่านั้น (สำหรับคุณ) ที่ฉันใช้คือauthenticationFactoryและappSettings:

  • authenticationFactoryเพียงจัดการล็อกอินของผู้ใช้ ในกรณีนี้ฉันใช้วิธีการเฉพาะเพื่อระบุว่าผู้ใช้เข้าสู่ระบบหรือไม่
  • appSettingsค่าคงที่เพียงเพื่อไม่ใช้สตริงทุกที่ appSettings.states.loginและappSettings.states.registerมีชื่อของรัฐสำหรับการเข้าสู่ระบบและการลงทะเบียน URL

จากนั้นในcontroller/ serviceetc ใดๆ คุณต้องใช้$stateบริการฉีดและคุณสามารถเข้าถึง URL ปัจจุบันและก่อนหน้าเช่นนี้:

  • ปัจจุบัน: $state.current.name
  • ก่อนหน้านี้: $state.previous.route.name

จากคอนโซล Chrome:

var injector = angular.element(document.body).injector();
var $state = injector.get("$state");
$state.current.name;
$state.previous.route.name;

การดำเนินงาน:

(ฉันใช้angular-ui-router v0.2.17และangularjs v1.4.9)

(function(angular) {
    "use strict";

    function $stateDecorator($delegate, $injector, $rootScope, appSettings) {
        function decorated$State() {
            var $state = $delegate;
            $state.previous = undefined;
            $rootScope.$on("$stateChangeSuccess", function (ev, to, toParams, from, fromParams) {
                $state.previous = { route: from, routeParams: fromParams }
            });

            $rootScope.$on("$stateChangeStart", function (event, toState/*, toParams, fromState, fromParams*/) {
                var authenticationFactory = $injector.get("authenticationFactory");
                if ((toState.name === appSettings.states.login || toState.name === appSettings.states.register) && authenticationFactory.isUserLoggedIn()) {
                    event.preventDefault();
                    $state.go(appSettings.states.index);
                }
            });

            return $state;
        }

        return decorated$State();
    }

    $stateDecorator.$inject = ["$delegate", "$injector", "$rootScope", "appSettings"];

    angular
        .module("app.core")
        .decorator("$state", $stateDecorator);
})(angular);

12

เพิ่มคุณสมบัติใหม่ที่เรียกว่า {previous} ไปยัง $ state บน $ stateChangeStart

$rootScope.$on( '$stateChangeStart', ( event, to, toParams, from, fromParams ) => {
    // Add {fromParams} to {from}
    from.params = fromParams;

    // Assign {from} to {previous} in $state
    $state.previous = from;
    ...
}

คุณสามารถใช้ $ state ได้ทุกที่ที่คุณต้องการก่อนหน้านี้

previous:Object
    name:"route name"
    params:Object
        someParam:"someValue"
    resolve:Object
    template:"route template"
    url:"/route path/:someParam"

และใช้งานได้เช่น:

$state.go( $state.previous.name, $state.previous.params );

9

ฉันติดอยู่กับปัญหาเดียวกันและหาวิธีที่ง่ายที่สุดในการทำเช่นนี้ ...

//Html
<button type="button" onclick="history.back()">Back</button>

หรือ

//Html
<button type="button" ng-click="goBack()">Back</button>

//JS
$scope.goBack = function() {
  window.history.back();
};

(ถ้าคุณต้องการให้ทดสอบได้มากกว่านี้ให้ฉีดบริการ $ window ลงในคอนโทรลเลอร์ของคุณและใช้ $ window.history.back ())


มันซับซ้อนมากขึ้นเมื่อคุณมีเมนูในแอปพลิเคชันของคุณ
Swanand

ไม่ได้รับปัญหาของคุณโปรดให้รายละเอียดเพิ่มเติม
NiRmaL

คุณจะสูญเสีย $ stateParams หากคุณไม่ผลักมันเข้าไปใน url ทางออกที่ดีกว่าคือบันทึก params ก่อนหน้าของคุณใน $ rootScope คุณสามารถรับได้จากที่นั่นและผลักดันเข้าสู่ $ state เพื่อย้อนกลับ
dangquang1020

คำตอบนี้ง่ายที่สุดสำหรับฉันที่จะใช้
Lalnuntluanga Chhakchhuak

8

ฉันใช้แนวทางที่คล้ายคลึงกับสิ่งที่ Endy Tjahjono ทำ

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

$state.go( 'state-whatever', { previousState : { name : $state.current.name } }, {} );

กุญแจนี่คือวัตถุ params (แผนที่ของพารามิเตอร์ที่จะถูกส่งไปยังรัฐ) -> { previousState : { name : $state.current.name } }

หมายเหตุ: โปรดทราบว่าฉันเพียงแค่ "บันทึก" แอตทริบิวต์ชื่อของวัตถุ $ state เพราะเป็นสิ่งเดียวที่ฉันต้องการสำหรับการบันทึกสถานะ แต่เราสามารถมีวัตถุทั้งหมดของรัฐ

จากนั้นระบุว่า "อะไรก็ตาม" ที่ระบุไว้ดังนี้:

.state( 'user-edit', {
  url : 'whatever'
  templateUrl : 'whatever',
  controller: 'whateverController as whateverController',
  params : {
    previousState: null,
  }
});

ที่นี่จุดสำคัญคือวัตถุ params

params : {
  previousState: null,
}

จากนั้นภายในสถานะนั้นเราจะได้สถานะก่อนหน้าดังนี้:

$state.params.previousState.name

6

นี่คือทางออกที่ยอดเยี่ยมจาก Chris Thielen ui-router-extras: $ previousState

var previous = $previousState.get(); //Gets a reference to the previous state.

previousเป็นวัตถุที่ดูเหมือนว่า: { state: fromState, params: fromParams }โดยที่ fromState เป็นสถานะก่อนหน้าและ fromParams เป็นพารามิเตอร์สถานะก่อนหน้า


1
เมื่อวันที่ 16 พฤษภาคม 2017 Chris Thielen ได้เพิ่มประกาศEnd of Life Noticeสำหรับโครงการ: ui-router-extras
Michael R

4

ตกลงฉันรู้ว่าฉันมางานปาร์ตี้ที่นี่ช้า แต่ฉันใหม่สำหรับเชิงมุม ฉันกำลังพยายามทำให้พอดีกับคำแนะนำแบบ John Papaที่นี่ ฉันต้องการทำให้สามารถใช้ซ้ำได้ดังนั้นฉันจึงสร้างเป็นบล็อก นี่คือสิ่งที่ฉันมาด้วย:

previousStateProvider

(function () {
'use strict';

angular.module('blocks.previousState')
       .provider('previousState', previousStateProvider);

previousStateProvider.$inject = ['$rootScopeProvider'];

function previousStateProvider($rootScopeProvider) {
    this.$get = PreviousState;

    PreviousState.$inject = ['$rootScope'];

    /* @ngInject */
    function PreviousState($rootScope) {
        $rootScope.previousParms;
        $rootScope.previousState;
        $rootScope.currentState;

        $rootScope.$on('$stateChangeSuccess', function (ev, to, toParams, from, fromParams) {
            $rootScope.previousParms = fromParams;
            $rootScope.previousState = from.name;
            $rootScope.currentState = to.name;
        });
    }
}
})();

core.module

(function () {
'use strict';

angular.module('myApp.Core', [
    // Angular modules 
    'ngMessages',
    'ngResource',

    // Custom modules 
    'blocks.previousState',
    'blocks.router'

    // 3rd Party Modules
]);
})();

core.config

(function () {
'use strict';

var core = angular.module('myApp.Core');

core.run(appRun);

function appRun(previousState) {
    // do nothing. just instantiating the state handler
}
})();

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


2

หากคุณต้องการฟังก์ชั่นนี้และต้องการใช้มันในคอนโทรลเลอร์มากกว่าหนึ่งตัวนี่เป็นบริการง่ายๆในการติดตามประวัติเส้นทาง

  (function () {
  'use strict';

  angular
    .module('core')
    .factory('RouterTracker', RouterTracker);

  function RouterTracker($rootScope) {

    var routeHistory = [];
    var service = {
      getRouteHistory: getRouteHistory
    };

    $rootScope.$on('$stateChangeSuccess', function (ev, to, toParams, from, fromParams) {
      routeHistory.push({route: from, routeParams: fromParams});
    });

    function getRouteHistory() {
      return routeHistory;
    }

    return service;
  }
})();

โดยที่ 'core' ใน. โมดูล ('core') จะเป็นชื่อของแอพ / โมดูลของคุณ ต้องใช้บริการเป็นการอ้างอิงไปยังคอนโทรลเลอร์ของคุณจากนั้นในคอนโทรลเลอร์ของคุณคุณสามารถทำได้:$scope.routeHistory = RouterTracker.getRouteHistory()


4
หากฉันมีปุ่มนำทางบนหน้าเว็บของฉันที่ไปยังสถานะก่อนหน้าในประวัติศาสตร์หลังจากนำทางไปยังสถานะก่อนหน้านั้น StateChangeSuccess จะถูกเปิดใช้งานซึ่งจะเพิ่มลงในประวัติ ดังนั้นรหัสนี้จะไม่สิ้นสุดทำให้วนซ้ำไปมาระหว่าง 2 หน้า?
whatsTheDiff

1

ฉันติดตามสถานะก่อนหน้านี้ใน$ rootScopeดังนั้นทุกครั้งที่ต้องการฉันจะโทรหาบรรทัดด้านล่างของรหัส

$state.go($rootScope.previousState);

ในApp.js :

$rootScope.$on('$stateChangeSuccess', function(event, to, toParams, from, fromParams) {
  $rootScope.previousState = from.name;
});

1
ฉันคิดว่ามันจะดีกว่าถ้าคุณประหยัดจากไม่ใช่แค่ชื่อ ด้วยวิธีนี้คุณจะมี params เช่นกันในกรณีที่คุณต้องทำอะไรบางอย่างเช่น $ state.go ($ tootScope.previousState.name, $ tootScope.previousState.params);
abelabbesnabi

0

สำหรับ UI-Router (> = 1.0) เหตุการณ์ StateChange ถูกเลิกใช้แล้ว สำหรับคำแนะนำในการโยกย้ายอย่างสมบูรณ์คลิกที่นี่

วิธีรับสถานะก่อนหน้าของสถานะปัจจุบันใน UI-Router 1.0+:

app.run(function ($transitions) {
    $transitions.onSuccess({}, function (trans) {
         // previous state and paramaters
         var previousState = trans.from().name;
         var previousStateParameters = trans.params('from');
    });
});

-1

วิธีแก้ปัญหาที่ง่ายมากคือการแก้ไขสตริง $ state.current.name และตัดทุกอย่างรวมถึงและหลังส่วนสุดท้าย '.' - คุณได้รับชื่อของรัฐแม่ วิธีนี้ใช้ไม่ได้หากคุณข้ามไปมาระหว่างรัฐเนื่องจากมันจะแยกวิเคราะห์เส้นทางปัจจุบัน แต่ถ้ารัฐของคุณตรงกับที่คุณอยู่จริงแล้วงานนี้

var previousState = $state.current.name.substring(0, $state.current.name.lastIndexOf('.'))
$state.go(previousState)

1
$state.go('^')จะประสบความสำเร็จนี้
james

และสิ่งที่เกี่ยวกับรัฐ params?
Tushar Shukla

-2

คุณสามารถคืนค่าสถานะด้วยวิธีนี้:

$state.go($state.$current.parent.self.name, $state.params);

ตัวอย่าง:

(function() {
    'use strict'

    angular.module('app')
        .run(Run);

    /* @ngInject */
    function Run($rootScope, $state) {

        $rootScope.back = function() {
            $state.go($state.$current.parent.self.name, $state.params);
        };

    };

})();

2
นั่นไม่ใช่คำตอบที่ดีหากคุณเข้าถึงสถานะจากรัฐที่ไม่ใช่ผู้ปกครอง หากคุณต้องการให้พาเรนต์ของสถานะปัจจุบันทำงานได้
Maxime Lafarie

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