问题
Is there a way to get the previous state of the current state?
For example I would like to know what the previous state was before current state B (where previous state would have been state A).
I am not able to find it in ui-router github doc pages.
回答1:
ui-router doesn't track the previous state once it transitions, but the event $stateChangeSuccess
is broadcast on the $rootScope
when the state changes.
You should be able to catch the prior state from that event (from
is the state you're leaving):
$rootScope.$on('$stateChangeSuccess', function (ev, to, toParams, from, fromParams) {
//assign the "from" parameter to something
});
回答2:
I use resolve to save the current state data before moving to the new state:
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;
}]
}
});
}]);
回答3:
For sake of readability, I'll place my solution (based of stu.salsbury's anwser) here.
Add this code to your app's abstract template so it runs on every page.
$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)
});
Keeps track of the changes in rootScope. Its pretty handy.
回答4:
In the following example i created a decorator
(runs only once per app at configuration phase) and adds an extra property to $state
service, so this approach does not add global variables to $rootscope
and does not require to add any extra dependency to other services than $state
.
In my example i needed to redirect a user to the index page when he was already signed in and when he was not to redirect him to the previous "protected" page after sign in.
The only unknown services (for you) that i use are authenticationFactory
and appSettings
:
authenticationFactory
just administrates the user login. In this case i use only a method to identify if the user is logged in or not.appSettings
are constants just for not use strings everywhere.appSettings.states.login
andappSettings.states.register
contain the name of the state for the login and register url.
Then in any controller
/service
etc you need to inject $state
service and you can access current and previous url like this:
- Current:
$state.current.name
- Previous:
$state.previous.route.name
From the Chrome console:
var injector = angular.element(document.body).injector();
var $state = injector.get("$state");
$state.current.name;
$state.previous.route.name;
Implementation:
(I'm using angular-ui-router v0.2.17
and 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);
回答5:
Add a new property called {previous} to $state on $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;
...
}
Now anywhere you need can use $state you will have previous available
previous:Object
name:"route name"
params:Object
someParam:"someValue"
resolve:Object
template:"route template"
url:"/route path/:someParam"
And use it like so:
$state.go( $state.previous.name, $state.previous.params );
回答6:
I am stuck with same issue and find the easiest way to do this...
//Html
<button type="button" onclick="history.back()">Back</button>
OR
//Html
<button type="button" ng-click="goBack()">Back</button>
//JS
$scope.goBack = function() {
window.history.back();
};
(If you want it to be more testable, inject the $window service into your controller and use $window.history.back()).
回答7:
I use a similar approach to what Endy Tjahjono does.
What I do is to save the value of the current state before making a transition. Lets see on an example; imagine this inside a function executed when cliking to whatever triggers the transition:
$state.go( 'state-whatever', { previousState : { name : $state.current.name } }, {} );
The key here is the params object (a map of the parameters that will be sent to the state) -> { previousState : { name : $state.current.name } }
Note: note that Im only "saving" the name attribute of the $state object, because is the only thing I need for save the state. But we could have the whole state object.
Then, state "whatever" got defined like this:
.state( 'user-edit', {
url : 'whatever'
templateUrl : 'whatever',
controller: 'whateverController as whateverController',
params : {
previousState: null,
}
});
Here, the key point is the params object.
params : {
previousState: null,
}
Then, inside that state, we can get the previous state like this:
$state.params.previousState.name
回答8:
Here is a really elegant solution from Chris Thielen ui-router-extras: $previousState
var previous = $previousState.get(); //Gets a reference to the previous state.
previous
is an object that looks like: { state: fromState, params: fromParams }
where fromState is the previous state and fromParams is
the previous state parameters.
回答9:
Ok, I know that I am late to the party here, but I am new to angular. I am trying to make this fit into the John Papa style guide here. I wanted to make this reusable so I created in a block. Here is what I came up with:
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
}
})();
Any critique on this code will only help me, so please let me know where I can improve this code.
回答10:
If you just need this functionality and want to use it in more than one controller, this is a simple service to track route history:
(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;
}
})();
where the 'core' in .module('core') would be the name of your app/module. Require the service as a dependency to your controller, then in your controller you can do: $scope.routeHistory = RouterTracker.getRouteHistory()
回答11:
I keep track of previous states in $rootScope, so whenever in need I will just call the below line of code.
$state.go($rootScope.previousState);
In App.js:
$rootScope.$on('$stateChangeSuccess', function(event, to, toParams, from, fromParams) {
$rootScope.previousState = from.name;
});
回答12:
For UI-Router(>=1.0), StateChange events have been deprecated. For complete migration guide, click here
To get the previous state of the current state 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');
});
});
回答13:
A really simple solution is just to edit the $state.current.name string and cut out everything including and after the last '.' - you get the name of the parent state. This doesn't work if you jump a lot between states because it just parses back the current path. But if your states correspond to where you actually are, then this works.
var previousState = $state.current.name.substring(0, $state.current.name.lastIndexOf('.'))
$state.go(previousState)
回答14:
You can return the state this way:
$state.go($state.$current.parent.self.name, $state.params);
An example:
(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);
};
};
})();
来源:https://stackoverflow.com/questions/16635381/angular-ui-router-get-previous-state