Angolare - ui-router ottiene lo stato precedente


152

C'è un modo per ottenere lo stato precedente dello stato corrente?

Ad esempio, vorrei sapere quale era lo stato precedente prima dello stato corrente B (dove lo stato precedente sarebbe stato lo stato A).

Non riesco a trovarlo nelle pagine del documento github di ui-router.


la risposta di seguito è corretta puoi anche trovare tutte le informazioni che ti servono dalla fonte se i documenti non sono sufficienti aiuto goo.gl/9B9bH
David Chase

Risposte:


133

ui-router non tiene traccia dello stato precedente dopo la transizione, ma l'evento $stateChangeSuccessviene trasmesso $rootScopequando lo stato cambia.

Dovresti essere in grado di rilevare lo stato precedente di quell'evento ( fromè lo stato che stai lasciando):

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

3
Quale sarebbe un esempio usando "from"?
Paul

Qui 'to' e 'from' significa 'toState' e 'fromState'. Considera che l'URL precedente è localhost: xxxx / employee e il controller è "EmployeesController", quindi l'esempio per "fromState" è: Object {url: "/ impiegati", templateUrl: "/ impiegati", controller: "EmployeesController", nome: " dipendenti "}
Ranadheer Reddy,

3
L'ho fatto nel mio abstract: `$ rootScope.previousState; $ rootScope.currentState; $ rootScope. $ on ('$ stateChangeSuccess', funzione (ev, to, toParams, from, fromParams) {$ rootScope.previousState = from.name; $ rootScope.currentState = to.name; console.log ('Stato precedente: '+ $ rootScope.previousState) console.log (' Stato corrente: '+ $ rootScope.currentState)}); `Tiene traccia di precedenti e attuali in rootScope. Molto utile!
Federico,

Utilizzando la soluzione di @ Endy-Tjahjono ( stackoverflow.com/a/25945003/2837519 1.x.) è più in linea di ui-router
Peter Ahlers,

Adoro un modo semplice con history.back () <a href="javascript:history.back()" class="btn btn-default no-radius">
Serip88

146

Uso la risoluzione per salvare i dati dello stato corrente prima di passare al nuovo stato:

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
molto meglio che affrontare$stateChangeSuccess
SET

10
Secondo me, questa dovrebbe essere la risposta accettata. Sebbene $stateChangeSuccessfunzioni, lo fa a livello globale, il che non è realmente necessario per la maggior parte del tempo.
Pierre Spring,

2
Sono d'accordo. Questo è molto più pulito. Se hai a che fare con molti moduli che hanno tutti stati, $ stateChangeSuccess verrà attivato per ogni cambio di stato, non solo quelli nel tuo modulo. Un altro voto perché questa è la soluzione migliore.
Jason Buchanan,

1
@ Nissassin17 quale versione usi? In v0.2.15 all'apertura uno stato direttamente $state.currentè oggetto: {name: "", url: "^", views: null, abstract: true}.
Endy Tjahjono,

3
Cordiali saluti - Avevo bisogno di fare quanto segue: Params: angular.copy ($ state.params) - Spiegazione: Sto usando UI-Router v 1.0.0-beta 2 e ho avuto problemi con la parte Params di questo codice; poiché $ state.params è un oggetto, si aggiornerà alla vista corrente dopo che la funzione è stata risolta ... dovevo farlo nella funzione di risoluzione per evitare che l'oggetto fosse aggiornato nella vista corrente.
Joe H,

100

Per motivi di leggibilità, inserirò qui la mia soluzione (basata sulla risposta di stu.salsbury).

Aggiungi questo codice al modello astratto dell'app in modo che venga eseguito su ogni pagina.

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

Tiene traccia delle modifiche in rootScope. È abbastanza utile.


19
Vale la pena conservare anche i fromParams se si desidera davvero reindirizzare qualcuno da dove sono venuti.
Yaron,

Adoro questo e implementarlo nelle mie applicazioni. Grazie
Fallenreaper il

Davvero utile ... Ancora di più usando localStorage, puoi ottenere lo stato precedente ovunque.
georgeos,

14

Nell'esempio seguente ho creato un decorator(viene eseguito una sola volta per app in fase di configurazione) e aggiunge una proprietà extra al $stateservizio, quindi questo approccio non aggiunge variabili globali$rootscope e non richiede alcuna dipendenza aggiuntiva da altri servizi oltre a$state .

Nel mio esempio avevo bisogno di reindirizzare un utente alla pagina dell'indice quando era già connesso e quando non doveva reindirizzare alla precedente pagina "protetta" dopo l'accesso.

Gli unici servizi sconosciuti (per te) che utilizzo sono authenticationFactorye appSettings:

  • authenticationFactorygestisce solo l'accesso dell'utente. In questo caso uso solo un metodo per identificare se l'utente ha effettuato l'accesso o meno.
  • appSettingssono costanti solo per non usare stringhe ovunque. appSettings.states.logine appSettings.states.registercontiene il nome dello stato per l'URL di accesso e di registrazione.

Quindi in qualsiasi controller/ serviceetc devi iniettare il $stateservizio e puoi accedere all'URL attuale e precedente in questo modo:

  • Attuale: $state.current.name
  • Precedente: $state.previous.route.name

Dalla console di Chrome:

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

Implementazione:

(Sto usando angular-ui-router v0.2.17e 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

Aggiungi una nuova proprietà chiamata {precedente} a $ state su $ 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;
    ...
}

Ora, ovunque tu abbia bisogno, puoi usare $ state avrai a disposizione in precedenza

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

E usalo così:

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

9

Sono bloccato con lo stesso problema e trovo il modo più semplice per farlo ...

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

O

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

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

(Se vuoi che sia più testabile, inietta il servizio $ window nel tuo controller e usa $ window.history.back ()).


è più complicato quando hai un menu nella tua applicazione
Swanand,

se non si verifica il problema, fornire maggiori dettagli
NiRmaL,

Perderai $ stateParams se non lo spingi in url, la soluzione migliore è salvare i tuoi parametri precedenti in $ rootScope, puoi ottenerlo e spingere nello stato $ per tornare indietro.
dangquang1020

Questa risposta è la più facile da implementare per me.
Lalnuntluanga Chhakchhuak,

8

Uso un approccio simile a quello che fa Endy Tjahjono.

Quello che faccio è salvare il valore dello stato corrente prima di effettuare una transizione. Vediamo un esempio; immagina questo all'interno di una funzione eseguita quando fai clic su qualunque cosa inneschi la transizione:

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

La chiave qui è l'oggetto params (una mappa dei parametri che verranno inviati allo stato) -> { previousState : { name : $state.current.name } }

Nota: nota che sto solo "salvando" l'attributo name dell'oggetto $ state, perché è l'unica cosa di cui ho bisogno per salvare lo stato. Ma potremmo avere l'intero oggetto stato.

Quindi, indica "qualunque cosa" sia stata definita in questo modo:

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

Qui, il punto chiave è l'oggetto params.

params : {
  previousState: null,
}

Quindi, all'interno di quello stato, possiamo ottenere lo stato precedente in questo modo:

$state.params.previousState.name

6

Ecco una soluzione davvero elegante di Chris Thielen ui-router-extra: $ previousState

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

previousè un oggetto simile: { state: fromState, params: fromParams }dove fromState è lo stato precedente e fromParams sono i parametri di stato precedenti.


1
Il 16 maggio 2017, Chris Thielen ha aggiunto un avviso di fine vita per il progetto: ui-router-extra.
Michael R,

4

Ok, so che sono in ritardo alla festa qui, ma sono nuovo di angolare. Sto cercando di adattarlo alla guida di stile di John Papa qui. Volevo renderlo riutilizzabile, quindi l'ho creato in un blocco. Ecco cosa mi è venuto in mente:

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

Qualsiasi critica su questo codice mi aiuterà solo, quindi per favore fatemi sapere dove posso migliorare questo codice.


2

Se hai solo bisogno di questa funzionalità e vuoi usarla in più di un controller, questo è un semplice servizio per tenere traccia della cronologia dei percorsi:

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

dove "core" in .module ("core") sarebbe il nome della tua app / modulo. Richiedi il servizio come dipendenza dal tuo controller, quindi nel tuo controller puoi fare:$scope.routeHistory = RouterTracker.getRouteHistory()


4
Se avessi un pulsante di navigazione sulla mia pagina che passa allo stato precedente nella cronologia, dopo essere passato a quello stato precedente, verrà attivato stateChangeSuccess che lo aggiunge alla cronologia. Quindi questo codice non finirebbe con il risultato di un ciclo infinito di andare avanti e indietro tra 2 pagine?
whatsTheDiff,

1

Tengo traccia degli stati precedenti in $ rootScope , quindi ogni volta che ho bisogno chiamerò semplicemente la riga di codice qui sotto.

$state.go($rootScope.previousState);

In App.js :

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

1
Penso che sia meglio se risparmi non solo da.name. In questo modo, avrai anche i parametri, nel caso in cui devi fare qualcosa come $ state.go ($ tootScope.previousState.name, $ tootScope.previousState.params);
abelabbesnabi,

0

Per UI-Router (> = 1.0), gli eventi StateChange sono stati deprecati. Per una guida completa alla migrazione, fai clic qui

Per ottenere lo stato precedente dello stato corrente in 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

Una soluzione davvero semplice è solo quella di modificare la stringa $ state.current.name e ritagliare tutto, incluso e dopo l'ultimo '.' - ottieni il nome dello stato principale. Questo non funziona se salti molto tra gli stati perché analizza semplicemente il percorso corrente. Ma se i tuoi stati corrispondono a dove sei realmente, allora funziona.

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

1
$state.go('^')ci riusciremo
Giacomo,

E i parametri di stato?
Tushar Shukla,

-2

È possibile restituire lo stato in questo modo:

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

Un esempio:

(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
Questa non è una buona risposta se si accede allo stato da uno stato non padre. Se vuoi solo il genitore dello stato corrente, fa il lavoro.
Maxime Lafarie,

1
Non è lo stesso che usare $ state.go ("^")?
Nathan Moinvaziri,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.