Skip to content

Instantly share code, notes, and snippets.

@machty
Last active December 15, 2015 23:19
Show Gist options
  • Select an option

  • Save machty/5338945 to your computer and use it in GitHub Desktop.

Select an option

Save machty/5338945 to your computer and use it in GitHub Desktop.
summary of ideas following saturday brainathon with Stefan, Luke, and Alex.

Proposal

  1. Make transitionTo private API, only meant to be called from within a route. The only external interaction with routes or the router should be through send events.
  2. Inject default routeTo handler in events hash of route:application which takes the arguments and .applys them to the route's private transitionTo.
  3. Change linkTo helper to emit a routeTo event (instead of transitionTo).
  4. Change handleURL to convert to a routeTo event
  5. Minor change to router.js to allow for event bubbling

handleURL Complications

handleURL is the method that responds to URL changes (back/forward navigation, etc.). We need to modify it so that it emits a routeTo event so that URL-changes too can be handled as Route actions (presently it bypasses any sound state machine principles and externally forces a transition).

So handleURL would need to take a URL change to:

/posts/123/comments/456

And implicitly convert it to

router.send('routeTo', 'postShow.commentShow', Post.find(123), Comment.find(456));

Post.find(456) and Comment.find(456) are returned from the respective deserializes of PostShowRoute and CommentShowRoute, but this is a simpler case. More complicated: what if CommentShowRoute's deserialize depends on PostShowRoute's deserialize having already been called?

One solution would be for handleURL to always provide promises as context objects. Instead of immediately calling deserialize on PostShowRoute, handleURL will create a promise that waits for parent routes' deserialize to be called, and when it resolves, it calls PostShowRoute's deserialize. And for the Comments route, provide a promise that calls CommentShowRoute's deserialize after PostShowRoute has been deserialized.

Why the complicated promise chain?

Mostly because there's no other way to funnel a handleURL into routeTo semantics (which is of course our major aim so that we can put states in charge of transitions in response to external events, rather than controllers/handleURL reaching in forcing us to transitionTo).

But, as an added bonus, this also gives a lot of control over how transitions occur in an application. For instance, with all attempted transitions (via application/user/navigation events) being funneled through the routeTo event, a route that handles form data can now intercept a routeTo event and make it no-op if a form is half-filled out.

Bubbling events

There needs to be a way to signal to router.js that a responded-to event should keep bubbling up to parent routes, so that, in our example above, a route can have logic in it that decides whether to prevent a transition, and if the transition should be allowed, the routeTo event gets passed up all the way to application:route. Probably the simplest way to do this would be to alter router.js so that if a handle returns true, the event continues to bubble. In the future, we can add some nicer API if really necessary.

What about a preventable transition DSL?

The above code would allow transition prevention by a source route, but not by a destination route (except via a post-transition redirect hook on the route), e.g., with the above code, an AdminRoute would not be able to prevent or redirect an attempted transition before it was already underway (and templates torn down, lots of other side effects, etc) without additional API, but this can be handled by a third party Ember package. It would basically override the routeTo on route:application to run through the transition event handlers as described in https://gist.github.com/machty/5170781 . But this would not need to be part of Ember Core any time soon.

After-thoughts

This will eliminate Ember's reliance on router.js's handleURL method, which has special logic in it that allows for the async resolution of promise contexts and the entering/exiting of loading states when promises are encountered. This changes in this proposal should be followed by a re-evaluating of how async promise resolution and sub-loading states should work, keeping in mind the goal that whatever the solution, it should be the same whether a transition was initiated by a user, application, or navigation event.

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