$stateChangeStart event continuing after state cha

2019-06-07 16:22发布

I'm attempting to send the user to a specific 'closed' UI State in Angular using the following:

$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
  // check the destination is active
  if(toState.data.isClosed) { // note that the 'closed' state has isClosed set to false
    event.preventDefault();
    $state.go('closed');
  }
  $rootScope.data = toState.data;  // getting executed twice after our state transition to 'closed'
  $log.debug(toState);
});

The issue I'm having is that $rootScope.data = toState.data is getting called twice AFTER we've transitioned to the 'closed' state.

On the first time $startChangeStart executes when navigating to our 'order' state with data.isClosed = true set in the router, the state is changed to 'closed' and the code in question doesn't get executed.

As we are changing states to 'closed' now, $startChangeStart gets triggered again, and the code in question executes for the first time with toState being our 'closed' state.

Strangely, the code is then subsequently executed, starting after the if() logic with toState being the original state of 'order'... meaning that when everything is loaded up, the $rootScope.data variable contains the data from 'order' rather than 'closed'. Adding a few breakpoints and the debugging code above confirms.

Any explanations?

Update

Due to the execution pattern with the state transition to the 'closed' state, I've added a return to ensure that the continued execution after the $state.go() call is terminated. Revised code:

$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
  // check the destination is active
  if(toState.data.isClosed) { // note that the 'closed' state has isClosed set to false
    event.preventDefault();
    $state.go('closed');
    return;
  }
  $rootScope.data = toState.data;  // getting executed twice after our state transition to 'closed'
  $log.debug(toState);
});

This is now working as expected, but I'm not sure it's 'correct'.

1条回答
我只想做你的唯一
2楼-- · 2019-06-07 16:45

The solution with return statement is simple correct. I created this playground, where you can test that.

Let's have these states. Firstly some state with isClosed false - which should not be managed by our event listener

  .state('state1', {
    url: "/state1",
    template: "<div>this is a state1</div>",
    data: { isClosed : false },
  })
  .state('state2', {
    url: "/state2",
    template: "<div>this is a state2</div>",
    data: { isClosed : false },
  })

and these will be caught and redirected

  .state('stateClosed1', {
    url: "/stateClosed1",
    template: "<div>this is a stateClosed1</div>",
    data: { isClosed : true },
  })
  .state('stateClosed2', {
    url: "/stateClosed2",
    template: "<div>this is a stateClosed2</div>",
    data: { isClosed : true },
  })
  // even this would be handy
  .state('closed', {
    url: "/closed",
    template: "<div>this is a closed</div>",
    data: { isClosed : false },
  })

Now, whenever we go to any state, the event $stateChangeStart is fired. So if we go to "not handled" states (state1, state2) - the event will be triggered once.

If we go to handled states (stateClosed1, stateClosed2), event is fired always twice:

  • go to stateClosed1
  • go to closed

Just to be sure: the event is really fired twice. And that's why we should write these listeners rather like this (get out ASAP):

  $rootScope.$on('$stateChangeStart', function(event, toState, toParams
                                                    , fromState, fromParams) {

    // get out if we already go to state, which is about to be handled below
    if(toState.name === "closed") {
      return;
    }

    // check the destination is active
    if (toState.data.isClosed) { // note that the 'closed' 
      event.preventDefault();    // state has isClosed set to false 
      $state.go('closed');
      return; 
    }
    $rootScope.data = toState.data; // getting executed 'closed'
                                    // twice after our state transition to 
                                    // only if above if does not contain return
    console.debug(toState);
  });

In fact, this solution (getting out as soon as possible), was issue in these Q & A:

查看更多
登录 后发表回答