英文原文: http://emberjs.com/guides/routing/preventing-and-retrying-transitions/

在一个路由过渡过程中,Ember路由器将一个过渡对象传递给过渡相关的路由的各种钩子。任何可以访问过渡对象的钩子,都可以通过调用transition.abort()来立即取消当前过渡,如果保存了该过渡对象,那么在后续可以通过调用transition.retry()来重新尝试过渡。

通过willTransition来阻止过渡

当尝试进行过渡时,无论是通过{{link-to}}transitionTo,还是改变URL,都会在当前活动的路由上触发一个willTransition操作。这给从页节点开始,每个活动的路由一个可以决定是否发生一个过渡的机会。

假设应用当前在一个提供了复杂的表单给用户填写的路由,用户却不小心回退了。那么除非过渡被阻止,否则用户在表单上的输入将全部丢失,这会导致极差的用户体验。

下面是一个可以处理这种情形的方案:

  1. App.FormRoute = Ember.Route.extend({
  2. actions: {
  3. willTransition: function(transition) {
  4. if (this.controllerFor('form').get('userHasEnteredData') &&
  5. !confirm("Are you sure you want to abandon progress?")) {
  6. transition.abort();
  7. } else {
  8. // Bubble the `willTransition` action so that
  9. // parent routes can decide whether or not to abort.
  10. return true;
  11. }
  12. }
  13. }
  14. });

当用户点击{{link-to}}助手时,或者应用使用transitionTo初始化一个过渡时,过渡将被取消并且URL也将保持不变。但是如果使用浏览器的后退按钮来退出FormRoute,或者如果用户手动的修改URL,那么在willTransition操作被调用之前,将被导航到新的URL。这样浏览器就会显示新的URl,即便在willTransition中调用了transition.abort()

modelbeforeModelafterModel中取消过渡

异步路由中描述的modelbeforeModelafterModel钩子,每一个被调用时都传入了一个过渡对象。这让目标路由可以取消尝试进行的过渡。

  1. App.DiscoRoute = Ember.Route.extend({
  2. beforeModel: function(transition) {
  3. if (new Date() < new Date("January 1, 1980")) {
  4. alert("Sorry, you need a time machine to enter this route.");
  5. transition.abort();
  6. }
  7. }
  8. });

保存并重试过渡

被取消的过渡可以在之后某一时刻进行重试。一个常见的场景就是有一个身份验证的路由,将未通过验证的用户重定向到登录页面,当用户完成登录后,将用户又定向回之前请求的路由。

  1. App.SomeAuthenticatedRoute = Ember.Route.extend({
  2. beforeModel: function(transition) {
  3. if (!this.controllerFor('auth').get('userIsLoggedIn')) {
  4. var loginController = this.controllerFor('login');
  5. loginController.set('previousTransition', transition);
  6. this.transitionTo('login');
  7. }
  8. }
  9. });
  10. App.LoginController = Ember.Controller.extend({
  11. actions: {
  12. login: function() {
  13. // Log the user in, then reattempt previous transition if it exists.
  14. var previousTransition = this.get('previousTransition');
  15. if (previousTransition) {
  16. this.set('previousTransition', null);
  17. previousTransition.retry();
  18. } else {
  19. // Default back to homepage
  20. this.transitionToRoute('index');
  21. }
  22. }
  23. }
  24. });