Skip to content

Instantly share code, notes, and snippets.

@machty
Last active December 14, 2015 21:09
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save machty/5149452 to your computer and use it in GitHub Desktop.
Save machty/5149452 to your computer and use it in GitHub Desktop.
Funnel through actions

Transition Handlers for Ember Router

Proposal

Add transitions hash to Ember.Route, which contains transition handlers for preventing/redirecting/decorating attempted transitions, whether initiated by transitionTo or URL change (handleURL).

Rationale

  1. Seal the app from unconstrained URL changes breaking your app's state machine
  2. Bring back router v1's state-based patterns while maintaining v2's lovely DSL for defining routes
  3. Cover a large plethora of corner cases with a simple API

Examples

App.Router.map(function() {
  this.resource('admin', function() { /* ... */ });
  this.resource('posts', function() { /* ... */ });
});

App.AdminRoute = Ember.Route.extend({
  transitions: {
    'from posts': function(e) {
      alert("You came here from one of PostRoute's children");
      
      // Prevent any transitions from Posts.
      e.preventTransition();

      // Will fire from:
      // /posts/favorites
      // /posts/show
      // /posts/34/foo

      // Will not fire from:
      // /fun
      // /
      // /admin/blorg
    },
    'from *': function(e) {
      alert("This will fire for every transitions into Admin");

      // Will fire from: 
      // /posts/123
      // /things/45
      // /borf
      // /

      // Will not fire from:
      // /admin/things
      // /admin/manage_users
      // any child of AdminRoute
    },
    'to posts': function(e) {
      // Redirect any attempts to transition into Posts
      e.transitionTo("somewhere.else", Thing.find(123));

      // Will fire when transitioning out of AdminRoute into:
      // /posts
      // /posts/132
      // /posts/favorites
    },
    'to *': function(e) {

      alert("Have fun wherever");

      // Will fire when transitioning out of AdminRoute into:
      // /
      // /posts
      // /anywhere
    }
  }
});

Use Cases

Redirect to Login if not authenticated

App.AdminRoute = Ember.Route.extend({
  transitions: {
    'from *': function(e) {
      // This will happen before any buildup/teardown of
      // templates has happened.
      if(!this.controllerFor("admin").get("loggedIn")) {
        e.transitionTo("login");
      }
    }
  }
});

Preventing Navigation Away from Partially Filled Form

This will catch both handleURLs and transitionTos. Previously this could have been done with transitionTos, but you would have had to go through the pain of converting each exit point to an {{action}} that could be captured. handleURLs could not be captured at all.

App.UserSignupRoute = Ember.Route.extend({
  transitions: {
    'to *': function(e) {
      if(this.controller.get('preventNavigation')) {
        e.preventTransition();
        this.controller.showNavigationConfirmPopup();
      }
    }
  }
});

Switching to replaceState When Appropriate

This is a specific use case from Discourse, but generally speaking, it's possible to alter/decorate the transition in various ways.

App.ThreadPostRoute = Ember.Route.extend({
  transitions: {
    'to thread.post': function(e) {
      // Note the self-reference.
      // This will catch any context changes within ThreadPost.
      if(e.params.thread_id === e.oldParams.thread_id) {
        e.urlMethod = 'replaceWith';
      }
    }
  }
});

In Conjunction w/ Routeless Substates, Animation

Routeless substates don't exist yet, but will soon. One likely use case (certain to be used by Yapp) is to use them for animation between routable states. All links and transitions could refer to the their intended final destination, and overridable transitions could very easily be used to inject intermediate states used for animation, event handling, etc.

Implementation

Most if not all of this can be implemented in router.js, which, similar to its knowledge of the events hash, will know to look for the transitions hash for transition overrides.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment