Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to be informed of state change cancellations in ui-router?

So I'm using UI-router in my application and im using the $stateChangeStart listener to show a loader and the $stateChangeSuccess to then hide the loader. This way I have a loader between state transitions.

$rootScope.$on('$stateChangeStart',
                function (event, toState, toParams, fromState, fromParams) {
///show loader
});

$rootScope.$on('$stateChangeSuccess',
                function (event, toState, toParams, fromState, fromParams) {
//hide loader
});

Problem:

  1. I'm in state A
  2. I click on a button to go to state B, $stateChangeStart is called for B, my loader is shown.
  3. Immediately I click on another button to go to state A (B has some stuff to resolve, so there's time before B's UI actually starts loading)
  4. B is not loaded, and $stateChangeSuccess is not called and my loader is never hidden (Since we were already in A, it isn't reloaded)

So is there a way I can listen for state change cancellations and hide my loader?

Update: I created a Plunk. In the plunk I've given a timeout of 10 seconds in B's resolve. So before it resolves, click on A. Monitor the console for state change listener events.

Update July 25, 2015 which uses latest AngularUI Router: Created new Plunk.

like image 333
Frozen Crayon Avatar asked Mar 16 '15 07:03

Frozen Crayon


1 Answers

The only sustainable solution I've found so far was to avoid using ui-sref. It masks the return value of $state.go which is key in this case.

The trick is to handle the transition promise returned by $state.go

$scope.safeGo = function(stateName){
  var transition = $state.go(stateName);
  transition.then(function(currentState){
    console.log('Transition finished successfully. State is ' + currentState.name);
  })
  .catch(function(err){
    //revert your loader animation here
    console.log('Transition failed. State is ' + $state.current.name, err);
  })
}

The same object can be accessed via $state.transition property however between transitions the property is null and in any of the events that $state broadcasts it is also null. Thus the only solid option is to grab the return value of $state.go.

When state A is selected before B was resolved the transition promise for B will reject with a reason Error: transition superseded. However you will still have to wait until B.resolve is complete. If you want to avoid that you will have to keep track of your states on your own, since transition to A will be resolved immediately (because current state is still A while B is trying to resolve).

Updated plunker

like image 53
Kirill Slatin Avatar answered Sep 28 '22 06:09

Kirill Slatin