Angular: el enrutador ui obtiene el estado anterior

152

¿Hay alguna manera de obtener el estado anterior del estado actual?

Por ejemplo, me gustaría saber cuál era el estado anterior al estado actual B (donde el estado anterior habría sido el estado A).

No puedo encontrarlo en las páginas de ui-router github doc.

Mihai H
fuente
la respuesta correcta es a continuación también se puede encontrar toda la información que necesita de la fuente si los documentos no son suficientes ayuda goo.gl/9B9bH
David Chase

Respuestas:

133

ui-router no rastrea el estado anterior una vez que realiza la transición, pero el evento $stateChangeSuccessse transmite $rootScopecuando el estado cambia.

Debería poder capturar el estado anterior de ese evento ( fromes el estado del que se va):

$rootScope.$on('$stateChangeSuccess', function (ev, to, toParams, from, fromParams) {
   //assign the "from" parameter to something
});
laurelnaiad
fuente
3
¿Cuál sería un ejemplo usando "desde"?
Paul
Aquí 'to' y 'from' significa 'toState' y 'fromState'. Considere que su url anterior es localhost: xxxx / employee y el controlador es 'EmployeesController', entonces el ejemplo de 'fromState' es: Object {url: "/ employee", templateUrl: "/ employee", controller: "EmployeesController", name: " empleados "}
Ranadheer Reddy
3
Hice esto en mi resumen: `$ rootScope.previousState; $ rootScope.currentState; $ rootScope. $ on ('$ stateChangeSuccess', function (ev, to, toParams, from, fromParams) {$ rootScope.previousState = from.name; $ rootScope.currentState = to.name; console.log ('Estado anterior: '+ $ rootScope.previousState) console.log (' Estado actual: '+ $ rootScope.currentState)}); `Lleva un registro de lo anterior y lo actual en rootScope. Bastante útil!
Federico
El uso de la solución @ endy-tjahjono ( stackoverflow.com/a/25945003/2837519 ) es más similar al ui-router 1.x.
Peter Ahlers
Me encanta una manera simple con history.back () <a href="javascript:history.back()" class="btn btn-default no-radius">
Serip88
146

Utilizo resolver para guardar los datos del estado actual antes de pasar al nuevo estado:

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;
                }]
            }
        });
}]);
Endy Tjahjono
fuente
8
mucho mejor que tratar$stateChangeSuccess
SET
10
En mi opinión, esta debería ser la respuesta aceptada. Aunque el evento $stateChangeSuccessfunciona, lo hace a nivel global, lo que realmente no se necesita la mayor parte del tiempo.
Pierre Spring
2
Estoy de acuerdo. Esto es mucho más limpio. Si se trata de muchos módulos que tienen estados, $ stateChangeSuccess se activará por cada cambio de estado, no solo los de su módulo. Otro voto para que esta sea la mejor solución.
Jason Buchanan
1
@ Nissassin17, ¿qué versión utilizas? En v0.2.15 al abrir un estado directamente el $state.currentes objeto: {name: "", url: "^", views: null, abstract: true}.
Endy Tjahjono
3
FYI: necesitaba hacer lo siguiente: Parámetros: angular.copy ($ state.params) - Explicación: Estoy usando UI-Router v 1.0.0-beta 2 y estaba teniendo problemas con la parte de Parámetros de este código; como $ state.params es un objeto, se actualizará a la vista actual después de que se resuelva la función ... necesitaba hacer esto en la función de resolución para evitar que el objeto se actualice en la vista actual.
Joe H
100

En aras de la legibilidad, colocaré aquí mi solución (basada en la respuesta de stu.salsbury).

Agregue este código a la plantilla abstracta de su aplicación para que se ejecute en cada página.

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

Realiza un seguimiento de los cambios en rootScope. Es bastante útil.

Federico
fuente
19
También vale la pena almacenar fromParams si realmente quieres redirigir a alguien de donde vino.
Yaron
Me encanta esto y lo implemento en mis aplicaciones. Gracias
Fallenreaper
Realmente útil ... Aún más usando localStorage, puede obtener el Estado previo en cualquier lugar.
georgeos
14

En el siguiente ejemplo, creé un decorator(se ejecuta solo una vez por aplicación en la fase de configuración) y agrega una propiedad adicional al $stateservicio, por lo que este enfoque no agrega variables globales$rootscope y no requiere agregar ninguna dependencia adicional a otros servicios que no sean $state.

En mi ejemplo, necesitaba redirigir a un usuario a la página de índice cuando ya había iniciado sesión y cuando no debía redirigirlo a la página "protegida" anterior después de iniciar sesión.

Los únicos servicios desconocidos (para usted) que uso son authenticationFactoryy appSettings:

  • authenticationFactorysolo administra el inicio de sesión del usuario. En este caso, solo uso un método para identificar si el usuario ha iniciado sesión o no.
  • appSettingsson constantes solo para no usar cadenas en todas partes. appSettings.states.loginy appSettings.states.registercontiene el nombre del estado para la URL de inicio de sesión y registro.

Luego, en cualquier controller/ serviceetc necesita inyectar $stateservicio y puede acceder a la URL actual y anterior de esta manera:

  • Actual: $state.current.name
  • Anterior: $state.previous.route.name

Desde la consola de Chrome:

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

Implementación:

(Estoy usando angular-ui-router v0.2.17y 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);
CodeArtist
fuente
12

Agregue una nueva propiedad llamada {anterior} a $ state en $ 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;
    ...
}

Ahora, en cualquier lugar que necesite, puede usar $ state que tendrá disponible previamente

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

Y úsalo así:

$state.go( $state.previous.name, $state.previous.params );
Luis
fuente
9

Estoy atrapado con el mismo problema y encuentro la forma más fácil de hacer esto ...

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

(Si desea que sea más comprobable, inyecte el servicio $ window en su controlador y use $ window.history.back ()).

NiRmaL
fuente
es más complicado cuando tienes un menú en tu aplicación
Swanand
no
entiendo
Perderá $ stateParams si no lo inserta en la url, la mejor solución es guardar sus parámetros anteriores en $ rootScope, puede obtenerlo de eso e ingresar al estado $ para volver.
dangquang1020
Esta respuesta es más fácil de implementar para mí.
Lalnuntluanga Chhakchhuak
8

Yo uso un enfoque similar a lo que hace Endy Tjahjono.

Lo que hago es guardar el valor del estado actual antes de hacer una transición. Veamos un ejemplo; imagine esto dentro de una función ejecutada al hacer clic en lo que desencadena la transición:

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

La clave aquí es el objeto params (un mapa de los parámetros que se enviarán al estado) -> { previousState : { name : $state.current.name } }

Nota: tenga en cuenta que solo estoy "guardando" el atributo de nombre del objeto $ state, porque es lo único que necesito para guardar el estado. Pero podríamos tener el objeto de todo el estado.

Luego, indique "lo que sea" se definió así:

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

Aquí, el punto clave es el objeto params.

params : {
  previousState: null,
}

Luego, dentro de ese estado, podemos obtener el estado anterior de esta manera:

$state.params.previousState.name
avcajaraville
fuente
6

Aquí hay una solución realmente elegante de Chris Thielen ui-router-extras: $ previousState

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

previouses un objeto que se parece a: { state: fromState, params: fromParams }fromState es el estado anterior y fromParams es los parámetros de estado anteriores.

Efraín
fuente
1
El 16 de mayo de 2017, Chris Thielen agregó un Aviso de fin de vida para el proyecto: ui-router-extras.
Michael R
4

Ok, sé que llego tarde a la fiesta aquí, pero soy nuevo en angular. Estoy tratando de hacer que esto encaje en la guía de estilo de John Papa aquí. Quería hacer esto reutilizable, así que creé en un bloque. Esto es lo que se me ocurrió:

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

módulo principal

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

Cualquier crítica a este código solo me ayudará, así que avíseme dónde puedo mejorar este código.

fizch
fuente
2

Si solo necesita esta funcionalidad y desea usarla en más de un controlador, este es un servicio simple para rastrear el historial de rutas:

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

donde el 'núcleo' en .module ('núcleo') sería el nombre de su aplicación / módulo. Requerir el servicio como una dependencia de su controlador, luego en su controlador puede hacer:$scope.routeHistory = RouterTracker.getRouteHistory()

usuario1970565
fuente
44
Si tuviera un botón de navegación en mi página que va al estado anterior en el historial, después de navegar a ese estado anterior, se activaría stateChangeSuccess, que lo agrega al historial. Entonces, ¿no terminaría este código resultando en un ciclo sin fin de ir y venir entre 2 páginas?
whatsTheDiff
1

Realizo un seguimiento de los estados anteriores en $ rootScope , por lo que siempre que lo necesite solo llamaré a la siguiente línea de código.

$state.go($rootScope.previousState);

En App.js :

$rootScope.$on('$stateChangeSuccess', function(event, to, toParams, from, fromParams) {
  $rootScope.previousState = from.name;
});
Naveen Kumar V
fuente
1
Creo que es mejor si guardas no solo from.name. De esa manera, también tendrá los parámetros, en caso de que necesite hacer algo como $ state.go ($ tootScope.previousState.name, $ tootScope.previousState.params);
abelabbesnabi
0

Para UI-Router (> = 1.0), los eventos StateChange han quedado en desuso. Para una guía completa de migración, haga clic aquí

Para obtener el estado anterior del estado actual en 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');
    });
});
NagarajuRaghava
fuente
-1

Una solución realmente simple es simplemente editar la cadena $ state.current.name y cortar todo, incluso después del último '.' - obtienes el nombre del estado padre. Esto no funciona si saltas mucho entre estados porque solo analiza la ruta actual. Pero si sus estados corresponden a donde realmente está, entonces esto funciona.

var previousState = $state.current.name.substring(0, $state.current.name.lastIndexOf('.'))
$state.go(previousState)
Juhana Pietari Lehtiniemi
fuente
1
$state.go('^')logrará esto
james
¿Y qué hay de los parámetros estatales?
Tushar Shukla
-2

Puede devolver el estado de esta manera:

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

Un ejemplo:

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

    };

})();
otaviodecampos
fuente
2
Esa no es una buena respuesta si está accediendo al estado desde un estado no padre. Si solo quiere el padre del estado actual, hace el trabajo.
Maxime Lafarie
1
¿No es lo mismo que usar $ state.go ("^")?
Nathan Moinvaziri