问题
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:
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:
- Angular ui router - Redirection doesn't work at all
- How can I fix 'Maximum call stack size exceeded' AngularJS
来源:https://stackoverflow.com/questions/27532388/statechangestart-event-continuing-after-state-change