Search code examples
ember.jsember-data

Undestanding Ember Route hooks to redirect


I have this router setup

this.route('dashboard', function () {
    this.route('numbers');
    this.route('workspaces', function () {
        this.route('workspace', {path: '/:wid'});
});

dashboard.workspaces is a link in navigation bar. When you click it loads dashboard.workspaces.workspace and immediately redirects to first subnav. I do it like this:

export default Ember.Route.extend({

     model: function() {
         return this.modelFor('dashboard');
     },

     afterModel: function(model) {
         this.transitionTo('dashboard.workspaces.workspace', model.get('workspaces.firstObject.id'));
     }
}

It's all good except when I'm already on dashboard.workspaces.workspace route and click dashboard.workspaces then "afterModel" hook is not called and I end up on dashboard.workspaces, which is unwanted empty page. Are there other hook that would be always called? I have already tried "activate" and few others with no luck.

Would be also nice to be able to save last subnav transition and redirect to in when click on main nav.


Solution

  • So it's important to think of the router as a stack. As you visit routes you push a route on/off the stack. An item that is being pushed on or off the stack will have events fired correlating to the transition at hand.

    In your particular case, the current implementation has two issues. The one issue you've already mentioned, when you pop off children routes the parent route doesn't fire any events. The second issue would occur under these circumstances.

    1. Navigate to workspaces /workspaces (it redirects to the first workspace /workspaces/1)
    2. Navigate to the second workspace /workspaces/2
    3. Refresh the page while on /workspaces/2 (while it's in the afterModel hook of workspaces it redirects to the first workspace /workspaces/1)

    So the easiest way to handle this is to create a child route under the workspaces route whose sole purpose is to redirect to the first workspace.

    this.route('dashboard', function () {
        this.route('numbers');
        this.route('workspaces', function () {
            this.route('first');
            this.route('workspace', {path: '/:wid'});
    });
    

    Remove the afterModel logic from the workspaces route and put it in the workspaces.first route.

    export default Ember.Route.extend({
      redirect: function() {
         var first = this.modelFor('workspaces').get('firstObject');
         this.transitionTo('dashboard.workspaces.workspace', first);
      }
    }
    

    Now in your template, instead of ever pointing to workspaces you would point to workspaces.first.

    {{link-to 'Workspaces' 'dashboard.workspaces.first'}}
    

    Now when you are in the dashboard and click it, it will push on the workspaces route into the stack execute its hooks. It will then push the first route onto the stack and execute its hooks. The hooks of the first route will cause it to pop the first route off the stack and push the workspace route onto the stack. When you try to click the workspaces route again it will pop workspace off the stack and then push first back onto the stack which will then again execute its hooks.

    Example: http://emberjs.jsbin.com/cipawapali/edit?html,js,output