Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

$state.go() in onEnter causes infinite redirect loop #1169

Closed
rockwood opened this issue Jun 27, 2014 · 11 comments
Closed

$state.go() in onEnter causes infinite redirect loop #1169

rockwood opened this issue Jun 27, 2014 · 11 comments

Comments

@rockwood
Copy link

We use the master/detail ui pattern in many of our states, and I commonly want to pre-populate the detail side with the first result from the server. The problem is that doing a state.go() in the onEnter hook results in an infinite loop. Is there a common way to do this?

example plunker: http://plnkr.co/edit/14pumrI7QoDhDD73kJQr?p=preview.

$stateProvider
  .state('posts', {
      url: "/posts",
      templateUrl: "posts-index.html",
      controller: "PostsIndexController",
      resolve: {
        posts: function(Post) {
          return Post.all()
        }
      },
      onEnter: function($state, posts) {
        if (posts.length > 1) {
          $state.go('posts.show', { postId: posts[0].id });
        }
      }
    })
    .state('posts.show', {
      url: "/:postId",
      templateUrl: "posts-show.html",
      controller: "PostsShowController",
      resolve: {
        post: function($stateParams, Post) {
          return Post.find($stateParams.postId)
        }
      }
    })
@sricc
Copy link

sricc commented Jul 2, 2014

Just ran into this as well. Seems like it only happens when it's a child state you try to transition to. Workaround was to put the check in the controller which I would rather not do....

@hahla
Copy link

hahla commented Jul 2, 2014

The cascade of states is always fun to handle (grin).

Here's a working plunkr
http://plnkr.co/edit/gFDcRqoPXKbD1ZZwylO9?p=preview

@rockwood
Copy link
Author

rockwood commented Jul 3, 2014

@hahla Your plunker doesn't seem to work. The problem is the state hasn't yet finished transitioning to posts so $state.current == ''.

@timkindberg
Copy link
Contributor

I can't quite figure out why you are doing this. What is the goal you are trying to achieve by sending them straight to the child state? Are you basically just wanting the first post to always be the default child state if they navigate to posts? If that's the goal, why not follow this solution? https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-set-up-a-defaultindex-child-state

I tested @hahla's plunkr and that worked for me.

@apreg
Copy link

apreg commented Jul 13, 2014

I also would like to see this solved.

@rockwood
Copy link
Author

@timkindberg Essentially we'd like to have a link to the first model in a collection (ie: "/posts/123") but since we haven't yet loaded posts from the server, we can't know which is the first post. The idea is to have the posts state first fetch all the posts and then redirect to posts.show with the id of the first post.

@christopherthielen
Copy link
Contributor

I think this is a documentation issue.

Any $state.transitionTo (or $state.go) that occurs while another transition is pending effectively cancels the pending transition and replaces it with the new one (TransitionSuperceded). onEnter, onExit, and state controllers are all invoked while the current transition is pending, thus any $state.go from those functions will supercede the pending transition and replace it. The new transition is then attempted and the onEnter/Exit/ctrl is invoked as usual.

Users should be aware of this because it is not intuitively obvious (myself included; I was bit by a similar use case with $state.go in a controller).

@rockwood perhaps you should be checking not if posts.length > 1, but rather if $stateParams.postId is defined?

I've created a pull request which detects the infinite loop and terminates the transitionTo request. This doesn't stop the loop from happening, but hopefully stops the dev from pulling their hair out.

@rockwood
Copy link
Author

I ended up using $timeout to delay the redirect until after the state transition has completed. It's pretty hackish, but #1257 should solve the issue when it lands.

onEnter: function($timeout, $state, posts) {
  if (posts.length > 1) {
    $timeout(function(){
      $state.go('posts.show', { postId: posts[0].id });
    })
  }
}

@christopherthielen
Copy link
Contributor

@rockwood I'm going to release ui-router-extras with $transition$ within a few days. It's probably going to be a while (0.3.0) before this ends up in main ui-router, and it will probably look different than my $transition$ implementation.

@Fr33maan
Copy link

Fr33maan commented May 6, 2015

@rockwood Hi, i don't really understand how can i be sure that the $state.go will be executed once the transition is over with the $timeout. I see that you did not give a delay, so in my logic it means that the function is executed whenever it is possible (maybe before end of transition). I suppose that i'm wrong because each time i refresh everything is ok but i would like to be sure.

thanks

@noblecraft
Copy link

@Prisonier the call to $timeout returns immediately and allows the onEnter() function to terminate normally without calling $state.go().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants