When using onEnter to redirect to a state, if the new state is a child of the current state, an infinite loop occurs.
Example:
$stateProvider
.state 'inventory',
url: '/inventory'
templateUrl: 'views/inventory.html'
controller: 'InventoryCtrl'
onEnter: () ->
$state.go 'inventory.low'
.state 'inventory.low',
url: '/low'
templateUrl: 'views/inventory-table.html'
controller: 'LowInventoryCtrl'
When:
$state.go 'inventory.low'
Is called, the state inventory
is re-initialized, causing it to be called again = infinite loop.
However, if the redirect state is:
$state.go 'otherStateThatIsNotAChild'
This issue does not occur. I assume that the parent state is being re-initialized, but why?
- Why is the parent state being reinitialized when
.go
is called on a child state? - How then, would you handle redirecting to a child state?
You need to step back and think about what you're trying to achieve. What's the point of having a state when all it's doing is redirecting to a child state?
Regarding your first question, parent states are always activated when you arrive at a child state, this behaviour is extremely useful in sharing data among states, and without it nested routing would be impossible (or rather wouldn't make sense).
As for the 2nd question, I've worked on a few big angular apps and so far I haven't found myself needing to do that.
OK, believe it or not, as much I'd hate to say it, right now I came across a scenario where I needed to do this. I have a
profile/userName
route (technically this should beprofile/userName/details
) and aprofile/userName/products
route, I wanted to have a master view for both states but at the same time I wanted theprofile/userName
route have a clean url, like:profile/mike62
, NOTprofile/mike62/details
. So I ended up doing this:Ended up achieving it like this, there are many ways though:
in my
publicProfile
state controller (this is the base state):Yes, it does feel hacky but now I know that there are some edge cases where we want to do this, althopugh we could rethink our state design altogether. Another, dirtier way would be to check the current state in a $timeout with a small delay inside the base state, if we are not on the
publicProfile.products
state, we navigate to our preferred/de-facto state ofpublicProfile.details
.While a transition is in process, any $state.go/transitionTo will cause the currently in process transition to be Superseded. An in-process transition that is superseded is cancelled immediately. Since your original transition to
inventory
is not completed by the time all the states' onEnters are called, the original transition is cancelled and a new transition toinventory.low
is started from the previously active state.See ui-router src https://github.com/angular-ui/ui-router/blob/master/src/state.js#L897 ...
You could...
$state.go
in a$timeout()
to allow the original transition to complete before redirecting.In any case, be very sure you want your app to redirect like this. If a user navigated directly to any other child state of inventory (such as
inventory.high
), the redirect will still occur, forcing them toinventory.low
which would not be what they intended.I had the same problem. The simple solution I found is to listen state changes and redirect to your child state from there.
It's kind of hack that makes routes redirecting to child states by default. It's not needed to do
url: ''
or$state.go()
, they don't work correctly.So, in
config
file:In
state
file:I've made an example on gist: https://gist.github.com/nikoloza/4ab3a94a3c6511bb1dac
After @Chris T's explanation, it seems that the cleanest solution is to listen for the
$stateChangeSuccess
event:Where redirects will contain any route redirects that may occur. Thanks all!
Don't make the low inventory state a child state.
It looks like you're trying to set a default child state.
That's a commonly asked question about ui-router: How to: Set up a default/index child state
The tl;dr is to use abstract states by settings
abstract: true
in the parent state. If you add multiple child states, it'll default to first child state.