home
  • Blog
3.1
  • Getting Started
  • Tutorial
  • The Object Model
  • Routing
    • Introduction
    • Defining Your Routes
    • Specifying a Route's Model
    • Rendering a Template
    • Redirecting
    • Preventing and Retrying Transitions
    • Loading / Error Substates
    • Query Parameters
    • Asynchronous Routing
  • Templates
  • Components
  • Controllers
  • Models
  • Application Concerns
  • Testing
  • Ember Inspector
  • Addons and Dependencies
  • Configuring Ember.js
  • Contributing to Ember.js
  • Glossary
Old Guides - You are viewing the guides for Ember v3.1.0.
Go to v5.0.0

Preventing and Retrying Transitions

Edit pencil

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().

Preventing Transitions via 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.

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:

app/routes/form.js
import Route from '@ember/routing/route';

export default Route.extend({
  actions: {
    willTransition(transition) {
      if (this.controller.get('userHasEnteredData') &&
          !confirm('Are you sure you want to abandon progress?')) {
        transition.abort();
      } else {
        // Bubble the `willTransition` action so that
        // parent routes can decide whether or not to abort.
        return true;
      }
    }
  }
});

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 route:form, 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().

Aborting Transitions Within model, beforeModel, afterModel

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.

app/routes/disco.js
import Route from '@ember/routing/route';

export default Route.extend({
  beforeModel(transition) {
    if (new Date() > new Date('January 1, 1980')) {
      alert('Sorry, you need a time machine to enter this route.');
      transition.abort();
    }
  }
});

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.

app/routes/some-authenticated.js
import Route from '@ember/routing/route';

export default Route.extend({
  beforeModel(transition) {
    if (!this.controllerFor('auth').get('userIsLoggedIn')) {
      let loginController = this.controllerFor('login');
      loginController.set('previousTransition', transition);
      this.transitionTo('login');
    }
  }
});
app/controllers/login.js
import Controller from '@ember/controller';

export default Controller.extend({
  actions: {
    login() {
      // Log the user in, then reattempt previous transition if it exists.
      let previousTransition = this.get('previousTransition');
      if (previousTransition) {
        this.set('previousTransition', null);
        previousTransition.retry();
      } else {
        // Default back to homepage
        this.transitionToRoute('index');
      }
    }
  }
});
left arrow
Redirecting
Loading / Error Substates
right arrow
On this page

  • Preventing Transitions via willTransition
  • Aborting Transitions Within model, beforeModel, afterModel
  • Storing and Retrying a Transition
Team Sponsors Security Legal Branding Community Guidelines
Twitter GitHub Discord Mastodon

If you want help you can contact us by email, open an issue, or get realtime help by joining the Ember Discord.

© Copyright 2023 - Tilde Inc.
Ember.js is free, open source and always will be.


Ember is generously supported by
blue