- Make
transitionToprivate API, only meant to be called from within a route. The only external interaction with routes or the router should be throughsendevents. - Inject default
routeTohandler ineventshash ofroute:applicationwhich takes the arguments and.applys them to the route's privatetransitionTo. - Change
linkTohelper to emit arouteToevent (instead oftransitionTo). - Change
handleURLto convert to arouteToevent - Minor change to
router.jsto allow for event bubbling
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.
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.
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.
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.
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.