During a route transition, the Ember Router passes a transition object to the various hooks on the routes involved in the transition. Any hook that has access to this transition object has the ability to immediately abort the transition by calling transition.abort(), and if the transition object is stored, it can be re-attempted at a later time by calling transition.retry().

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

Preventing Transitions via willTransition

通过willTransition来阻止过渡

When a transition is attempted, whether via {{link-to}}, transitionTo, or a URL change, a willTransition action is fired on the currently active routes. This gives each active route, starting with the leaf-most route, the opportunity to decide whether or not the transition should occur.

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

Imagine your app is in a route that’s displaying a complex form for the user to fill out and the user accidentally navigates backwards. Unless the transition is prevented, the user might lose all of the progress they made on the form, which can make for a pretty frustrating user experience.

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

Here’s one way this situation could be handled:

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

  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. });

When the user clicks on a {{link-to}} helper, or when the app initiates a transition by using transitionTo, the transition will be aborted and the URL will remain unchanged. However, if the browser back button is used to navigate away from FormRoute, or if the user manually changes the URL, the new URL will be navigated to before the willTransition action is called. This will result in the browser displaying the new URL, even if willTransition calls transition.abort().

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

Aborting Transitions Within model, beforeModel, afterModel

modelbeforeModelafterModel中取消过渡

The model, beforeModel, and afterModel hooks described in Asynchronous Routing each get called with a transition object. This makes it possible for destination routes to abort attempted transitions.

异步路由中描述的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. });

Storing and Retrying a Transition

保存并重试过渡

Aborted transitions can be retried at a later time. A common use case for this is having an authenticated route redirect the user to a login page, and then redirecting them back to the authenticated route once they’ve logged in.

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

  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. });