问题
I have two states, A and B.
- When I exit state A by clicking on a close button I do a transition to screen A using $state.go to state B (screen B)
- When I exit state A by clicking on the back browser button on screen A I do a transition to state B (screen B) as the browser URL changes
In the onExit of screen A I do a check and if this fails an Error Dialog opens, clicking Close on the error dialog returns a failed promise to the onExit
However the onExit still goes ahead and I go to screen B
Is it possible to stop the transition from State A (Screen A) to State B (Screen B) if something fails in the onExit?
回答1:
You can achieve your goal implementing a $stateChangeStart event handler in which you can cancel the state transition (event.preventDefault();
) when needed.
Here below is an example where a checkBox simulate a condition of transition disabled. In this case on state change (both state.go and navigation) it opens a modal asking the user to accept/reject the transition blocked (another simulation of some validation check):
var myApp = angular.module('myApp', ['ui.router','ui.bootstrap']);
myApp.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/state_a');
$stateProvider
.state('state_a', {
url: '/state_a',
templateUrl: 'stateA.html',
onEnter: function() {
console.log("onEnter stateA");
},
onExit: function($rootScope, $state) {
console.log("onExit stateA: "+$rootScope.chk.transitionEnable);
},
controller: function($scope, $state) {
$scope.goToStateB = function() {
$state.go("state_b");
}
}
})
.state('state_b', {
url: '/state_b',
templateUrl: 'stateB.html',
onEnter: function() {
console.log("onEnter stateB");
},
onExit: function() {
console.log("onExit stateB");
},
controller: function($scope) {
}
});
});
myApp.controller('mainCtrl', function($rootScope, $scope, $uibModal, $state) {
$rootScope.chk = {};
$rootScope.chk.transitionEnable = true;
$rootScope.$on('$stateChangeStart',
function(event, toState, toParams, fromState, fromParams, options) {
if (!$rootScope.chk.transitionEnable) {
event.preventDefault();
$scope.toState = toState;
$scope.open();
} else {
console.log("$stateChangeStart: "+toState.name);
}
})
$scope.open = function () {
var modalInstance = $uibModal.open({
animation: $scope.animationsEnabled,
templateUrl: 'myModal.html',
scope: $scope,
controller: function($uibModalInstance, $scope) {
$scope.ok = function () {
$uibModalInstance.close('ok');
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
},
size: 'sm'
});
modalInstance.result.then(function (value) {
console.info('Modal closed: ' + value);
}, function () {
console.info('Modal dismissed');
$rootScope.chk.transitionEnable = true;
$state.go($scope.toState.name);
});
};
});
<!DOCTYPE html>
<html>
<head>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular-animate.js"></script>
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-1.1.1.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.17/angular-ui-router.min.js"></script>
<script src="app.js"></script>
</head>
<body ng-app="myApp" ng-controller="mainCtrl">
<nav class="navbar navbar-inverse" role="navigation">
<div class="navbar-header">
<a class="navbar-brand" ui-sref="#">AngularUI Router</a>
</div>
<ul class="nav navbar-nav">
<li><a ui-sref="state_a">State A</a></li>
<li><a ui-sref="state_b">State B</a></li>
</ul>
</nav>
<div class="container">
<div ui-view></div>
</div>
<script type="text/ng-template" id="myModal.html">
<div class="modal-header">
<h3 class="modal-title">Title</h3>
</div>
<div class="modal-body">
Transition to <b>{{toState.name}}</b> is disabled, accept or ignore?
</div>
<div class="modal-footer">
<button class="btn btn-primary" type="button" ng-click="ok()">Accept</button>
<button class="btn btn-warning" type="button" ng-click="cancel()">Ignore</button>
</div>
</script>
<script type="text/ng-template" id="stateA.html">
<div class="jumbotron text-center">
<h1>state A</h1>
<p>Example</p>
<a class="btn btn-primary" ng-click="goToStateB()">Goto State B</a>
<a class="btn btn-danger">Do nothing...</a>
</div>
<div class="checkbox">
<label>
<input type="checkbox" ng-model="chk.transitionEnable"> Enable transition
</label>
<pre>chk.transitionEnable = {{chk.transitionEnable}}</pre>
</div>
</script>
<script type="text/ng-template" id="stateB.html">
<div class="jumbotron text-center">
<h1>state B</h1>
<p>The page... </p>
</div>
<div class="container">
Bla bla
<div class="checkbox">
<label>
<input type="checkbox" ng-model="chk.transitionEnable"> Enable transition
</label>
<pre>chk.transitionEnable = {{chk.transitionEnable}}</pre>
</div>
</div>
</script>
</body>
</html>
回答2:
I answered a similar question where you can prevent the state transition, you might find it useful
$scope.$on('$stateChangeStart') and $modal dialog
回答3:
@beaver approach definitely works well if the logic decides on the transition to the state B
resides in the state A
controller.
However if is the case that the state transition to state B
requires a pre-condition to be true (regardless of the current state), then using ui-router Resolve could be a cleaner solution.
The business logic for the pre-condition could live in a separate service.
This setup also allows the state B
controller to receive the resolved value, if required. And since the resolve function uses promises, the process can be asynchronous as well.
angular.module("yourAppModule")
.service('StateB_helperService', [
'$q',
function($q) {
this.allowTransition = function() {
var deferred = $q.defer();
//
// ... state transition logic ...
//
// resolves the promise to allow the state transition
// rejects otherwise
//
return deferred.promise;
}
}
])
...
angular.module("yourAppModule")
.config([
'$stateProvider',
function($stateProvider) {
$stateProvider.state('state_b', {
templateUrl: "...",
resolve: {
stateB_PreCondition: [
'StateB_helperService',
function(StateB_helperService) {
return StateB_helperService.allowTransition();
}
]
}
})
}
])
来源:https://stackoverflow.com/questions/35203909/can-i-stop-the-transition-to-the-next-state-in-an-onexit