Angular - ui-router mendapatkan status sebelumnya


152

Apakah ada cara untuk mendapatkan status sebelumnya dari kondisi saat ini?

Misalnya saya ingin tahu apa keadaan sebelumnya sebelum keadaan saat ini B (di mana keadaan sebelumnya adalah keadaan A).

Saya tidak dapat menemukannya di halaman doc ui-router github.


jawaban di bawah ini benar, Anda juga dapat menemukan semua info yang Anda butuhkan dari sumber jika dokumen tidak cukup membantu goo.gl/9B9bH
David Chase

Jawaban:


133

ui-router tidak melacak keadaan sebelumnya setelah transisi, tetapi acara $stateChangeSuccessdisiarkan pada $rootScopesaat keadaan berubah.

Anda harus dapat mengetahui keadaan sebelumnya dari peristiwa itu ( fromapakah Anda akan pergi):

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

3
Apa yang akan menjadi contoh menggunakan "dari"?
Paul

Di sini 'to' dan 'from' berarti 'toState' dan 'fromState'. Pertimbangkan url Anda sebelumnya adalah localhost: xxxx / employee dan controller adalah 'EmployeesController' maka contoh untuk 'fromState' adalah: Object {url: "/ karyawan", templateUrl: "/ karyawan", controller: "EmployeesController", nama: " karyawan "}
Ranadheer Reddy

3
Saya melakukan ini dalam abstrak saya: `$ rootScope.previousState; $ rootScope.currentState; $ rootScope. $ on ('$ stateChangeSuccess', fungsi (ev, to, toParams, from, fromParams) {$ rootScope.previousState = from.name; $ rootScope.currentState = to.name; console.log ('Status sebelumnya: '+ $ rootScope.previousState) console.log (' Keadaan saat ini: '+ $ rootScope.currentState)}); `Ini melacak sebelumnya dan saat ini di rootScope. Cukup berguna!
Federico

Menggunakan solusi @ endy-tjahjono ( stackoverflow.com/a/25945003/2837519 ) lebih sesuai dengan ui-router 1.x.
Peter Ahlers

Saya suka cara sederhana dengan history.back () <a href="javascript:history.back()" class="btn btn-default no-radius">
Serip88

146

Saya menggunakan tekad untuk menyimpan data status saat ini sebelum pindah ke status baru:

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
jauh lebih baik daripada berurusan dengan$stateChangeSuccess
SET

10
Menurut pendapat saya, ini harus menjadi jawaban yang diterima. Meskipun $stateChangeSuccessbekerja, ia melakukannya di tingkat global, yang sebenarnya tidak terlalu dibutuhkan.
Pierre Spring

2
Saya setuju. Ini jauh lebih bersih. Jika Anda berurusan dengan banyak modul yang semuanya memiliki status, $ stateChangeSuccess akan dipecat untuk setiap perubahan status, bukan hanya yang ada di modul Anda. Pilihan lain untuk ini adalah solusi yang lebih baik.
Jason Buchanan

1
@ Nissassin17 versi apa yang Anda gunakan? Dalam v0.2.15 saat membuka sebuah negara secara langsung $state.currentadalah objek: {name: "", url: "^", views: null, abstract: true}.
Endy Tjahjono

3
FYI - Saya perlu melakukan hal berikut: Params: angular.copy ($ state.params) - Penjelasan: Saya menggunakan UI-Router v 1.0.0-beta 2 dan saya mengalami masalah dengan bagian Params dari kode ini; karena $ state.params adalah objek, ia akan memperbarui ke tampilan saat ini setelah fungsi teratasi ... saya perlu melakukan ini dalam fungsi resolusi untuk mencegah objek diperbarui pada tampilan saat ini.
Joe H

100

Demi keterbacaan, saya akan menempatkan solusi saya (berdasarkan stu.salsbury's anwser) di sini.

Tambahkan kode ini ke templat abstrak aplikasi Anda sehingga itu berjalan di setiap halaman.

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

Melacak perubahan di rootScope. Cukup praktis.


19
Berguna untuk menyimpan fromParams juga jika Anda benar-benar ingin mengarahkan seseorang kembali ke tempat asalnya.
Yaron

Saya suka ini dan menerapkannya di aplikasi saya. Terima kasih
Fallenreaper

Sangat berguna ... Bahkan lebih banyak menggunakan localStorage, Anda bisa mendapatkan Negara sebelumnya di mana saja.
georgeos

14

Dalam contoh berikut ini saya membuat a decorator (berjalan hanya sekali per aplikasi pada tahap konfigurasi) dan menambahkan properti tambahan ke $statelayanan, sehingga pendekatan ini tidak menambahkan variabel global$rootscope dan tidak perlu menambahkan ketergantungan ekstra ke layanan lain selain $state.

Dalam contoh saya, saya perlu mengarahkan pengguna ke halaman indeks ketika dia sudah masuk dan ketika dia tidak mengarahkannya ke halaman "terlindungi" sebelumnya setelah masuk.

Satu-satunya layanan tidak dikenal (untuk Anda) yang saya gunakan adalah authenticationFactory dan appSettings:

  • authenticationFactorybaru saja mengatur login pengguna. Dalam hal ini saya hanya menggunakan metode untuk mengidentifikasi apakah pengguna masuk atau tidak.
  • appSettingsadalah konstanta hanya untuk tidak menggunakan string di mana-mana. appSettings.states.logindan appSettings.states.registerberisi nama negara bagian untuk login dan mendaftar url.

Kemudian di controller/ serviceetc Anda perlu menyuntikkan $statelayanan dan Anda dapat mengakses url saat ini dan sebelumnya seperti ini:

  • Arus: $state.current.name
  • Sebelumnya: $state.previous.route.name

Dari konsol Chrome:

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

Penerapan:

(Saya menggunakan angular-ui-router v0.2.17dan 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

Tambahkan properti baru yang disebut {sebelumnya} ke $ state di $ 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;
    ...
}

Sekarang di mana saja Anda perlu dapat menggunakan $ state yang sebelumnya Anda miliki

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

Dan gunakan seperti ini:

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

9

Saya terjebak dengan masalah yang sama dan menemukan cara termudah untuk melakukan ini ...

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

ATAU

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

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

(Jika Anda ingin lebih bisa diuji, masukkan layanan $ window ke controller Anda dan gunakan $ window.history.back ()).


ini lebih rumit ketika Anda memiliki menu di aplikasi Anda
Swanand

tidak mendapatkan masalah Anda, berikan lebih detail
NiRmaL

Anda akan kehilangan $ stateParams jika Anda tidak mendorongnya ke url, solusi yang lebih baik adalah menyimpan param Anda sebelumnya di $ rootScope, Anda bisa mendapatkannya dari itu dan mendorong ke dalam $ state untuk kembali.
dangquang1020

Jawaban ini paling mudah untuk saya terapkan.
Lalnuntluanga Chhakchhuak

8

Saya menggunakan pendekatan yang mirip dengan apa yang dilakukan Endy Tjahjono.

Apa yang saya lakukan adalah menyimpan nilai kondisi saat ini sebelum melakukan transisi. Mari kita lihat pada contoh; bayangkan ini di dalam suatu fungsi yang dieksekusi ketika melakukan cliking pada apa pun yang memicu transisi:

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

Kuncinya di sini adalah objek params (peta parameter yang akan dikirim ke negara) -> { previousState : { name : $state.current.name } }

Catatan: perhatikan bahwa saya hanya "menyimpan" atribut nama objek $ state, karena satu-satunya yang saya perlukan untuk menyimpan state. Tapi kita bisa memiliki objek seluruh negara.

Kemudian, sebutkan "apa pun" yang didefinisikan seperti ini:

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

Di sini, titik kuncinya adalah objek params.

params : {
  previousState: null,
}

Kemudian, di dalam keadaan itu, kita bisa mendapatkan keadaan sebelumnya seperti ini:

$state.params.previousState.name

6

Berikut ini adalah solusi yang benar-benar elegan dari Chris Thielen ui-router-ekstra: $ priorState

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

previousadalah objek yang terlihat seperti: { state: fromState, params: fromParams }where fromState adalah status sebelumnya dan fromParams adalah parameter status sebelumnya.


1
Pada 16 Mei 2017, Chris Thielen menambahkan Pemberitahuan Akhir Hidup untuk proyek: ui-router-extras.
Michael R

4

Ok, saya tahu bahwa saya terlambat ke pesta di sini, tetapi saya baru untuk bersudut. Saya mencoba menjadikan ini cocok dengan panduan gaya John Papa di sini. Saya ingin membuat ini dapat digunakan kembali, jadi saya buat di blok. Inilah yang saya pikirkan:

Penyedia Negara sebelumnya

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

Setiap kritik pada kode ini hanya akan membantu saya, jadi tolong beri tahu saya di mana saya dapat meningkatkan kode ini.


2

Jika Anda hanya memerlukan fungsi ini dan ingin menggunakannya di lebih dari satu pengontrol, ini adalah layanan sederhana untuk melacak riwayat rute:

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

di mana 'core' di .module ('core') akan menjadi nama aplikasi / modul Anda. Membutuhkan layanan sebagai ketergantungan pada controller Anda, maka di controller Anda Anda dapat melakukan:$scope.routeHistory = RouterTracker.getRouteHistory()


4
Jika saya memiliki tombol navigasi di halaman saya yang pergi ke keadaan sebelumnya dalam sejarah, setelah menavigasi ke keadaan sebelumnya, stateChangeSuccess akan dipecat yang menambahkannya ke sejarah. Jadi bukankah kode ini berakhir dengan loop tak berujung bolak-balik antara 2 halaman?
whatsTheDiff

1

Saya melacak status sebelumnya di $ rootScope , jadi kapan pun saya membutuhkan, saya hanya akan memanggil baris kode di bawah ini.

$state.go($rootScope.previousState);

Di App.js :

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

1
Saya pikir lebih baik jika Anda menyimpan dari tidak hanya dari. Nama. Dengan begitu, Anda akan memiliki params juga, jika Anda perlu melakukan sesuatu seperti $ state.go ($ tootScope.previousState.name, $ tootScope.previousState.params);
abelabbesnabi

0

Untuk UI-Router (> = 1.0), acara StateChange telah ditinggalkan. Untuk panduan migrasi lengkap, klik di sini

Untuk mendapatkan status sebelumnya dari kondisi saat ini di 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

Solusi yang sangat sederhana adalah hanya mengedit string $ state.current.name dan memotong semuanya termasuk dan setelah yang terakhir '.' - Anda mendapatkan nama negara induk. Ini tidak berfungsi jika Anda melompat banyak antar negara karena hanya mem-parsing kembali jalur saat ini. Tetapi jika negara Anda sesuai dengan tempat Anda sebenarnya, maka ini berfungsi.

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

1
$state.go('^')akan mencapai ini
james

Dan bagaimana dengan params negara?
Tushar Shukla

-2

Anda dapat mengembalikan negara dengan cara ini:

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

Sebuah contoh:

(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
Itu bukan jawaban yang baik jika Anda mengakses negara dari keadaan bukan orangtua. Jika Anda hanya ingin orang tua dari kondisi saat ini, itu berfungsi.
Maxime Lafarie

1
Bukankah ini sama dengan menggunakan $ state.go ("^")?
Nathan Moinvaziri
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.