Search code examples
ember.jsember-old-router

How to route to the same nested state but have a different context?


I currently struggle to get a nested route to work, where one of the path elements is dynamic. That's the scenario I want to achieve:

The page contains the description of a project. Within the page is a tab menu to select different views. That should reflect in the URL as well. So I want to have different urls like:

url#/project1/info
url#/project1/status
url#/project1/...

To not repeat the :project parameter I added a nested project route that is not a leaf but only responsible for serialization/deserialization of the project itself. Everything works fine as long as I use the initial project. But it can happen, that I want to link from one project to another project. That means the URL should change from url#/project1/info -> url#/project2/info and thus the view should change as well to display the infos about project2.

Sounds straightforward. However the deserialization method of the project route is not called when I link to project2 with an action helper

<a {{action changeProject context="App.project2" href=true}}>Go to project 2</a>

I guess that is because I am already in the info state. However how do I then propagate the context change? A simplified case you can find in the fiddle http://jsfiddle.net/jocsch/HYbZj/30/ or view it directly http://jsfiddle.net/jocsch/HYbZj/30/show/#/project1/info

Router: Ember.Router.extend({
    enableLogging: true,
    root: Ember.Route.extend({
        changeProject: Em.State.transitionTo('project.info'),                 
        index: Ember.Route.extend({
          route: '/',
         }),
         project: Ember.Route.extend({
           route: '/:project', 
           deserialize: function(router, params) {
              var proj = App.get(params['project']);
              router.get("applicationController").set("content", proj);
              return proj;
           },
           serialize: function(router, context) {
               return {project: context.id};
           },
           index: Ember.Route.extend({
              route: '/',
              redirectsTo: 'info'   
            }),                                    
            info: Ember.Route.extend({
              route: '/info',
              connectOutlets: function(router) {
                var ctrl = router.get('applicationController');
                ctrl.connectOutlet('project', ctrl.get('content'));
             }
         })
      })
   })
})

Solution

  • You do not need any custom serialization/deserialization.

    One important missing thing in your code is the context passing in changeProject handler.

    I would write the whole thing as follow:

    JS

    App = Ember.Application.create();
    
    App.Project = DS.Model.extend({
        name: DS.attr('string'),
        description: DS.attr('string')
    });
    
    App.Project.FIXTURES = [{
        id: '36',
        name: 'First project',
        description: 'My very first project'
    }, {
        id: '42',
        name: 'Another project',
        description: 'My other favorite project'
    }];
    
    App.store = DS.Store.create({
        adapter: DS.fixtureAdapter,
        revision: 4
    });
    
    
    App.ApplicationController = Ember.Controller.extend();
    App.ApplicationView = Ember.View.extend({
        templateName: 'app-view'
    })
    
    App.ProjectsController = Ember.ArrayController.extend();
    App.ProjectsView = Ember.View.extend({
        templateName: 'projects-view'
    })
    
    App.ProjectController = Ember.ObjectController.extend();
    App.ProjectView = Ember.View.extend({
        templateName: 'project-view'
    })
    
    App.InfoController = Ember.ObjectController.extend();
    App.InfoView = Ember.View.extend({
        templateName: 'info-view'
    })
    
    App.Router = Ember.Router.extend({
        enableLogging: true,
    
        root: Ember.Route.extend({
            index: Ember.Route.extend({
                route: '/'
            }),
    
            showProjects: function(router) {
                router.transitionTo('projects.index');
            }, 
    
            projects: Ember.Route.extend({
                route: 'projects',
    
                connectOutlets: function(router) {
                    var applicationController = router.get('applicationController');
                    applicationController.connectOutlet({
                        outletName: 'projectsList',
                        name: 'projects',
                        context: App.Project.find()
                    });
                },
    
                index: Ember.Route.extend({
                    route: '/'
                }),
    
                showProject: function(router, event) {
                    var project = event.context;
                    router.transitionTo('project.info', project);
                },
    
                project: Ember.Route.extend({
                    route: '/:project_id',
                    modelClass: 'App.Project',
    
                    connectOutlets: function(router, project) {
                        var applicationController = router.get('applicationController');
                        applicationController.connectOutlet('project', project);
                    },
    
                    info: Ember.Route.extend({
                        route: 'info',
    
                        connectOutlets: function(router) {
                            var projectController = router.get('projectController'),
                                project = projectController.get('content');
                            projectController.connectOutlet('info', project);
                        }
                    })
                })
            })
        })
    });
    
    App.initialize();
    

    Handlebars

    <script type="text/x-handlebars" data-template-name='app-view'>
        <h1>Welcome to projects app!</h1>
        <a {{action showProjects}}>Projects home</a>
        <hr/>
        {{outlet projectsList}}
        <hr/>
        {{outlet}}
    </script>
    
    <script type="text/x-handlebars" data-template-name='projects-view'>
        {{controller.length}} projects:
        <ul>
        {{#each project in controller}}
            <li>
                <a {{action showProject context="project"}}>{{project.name}}</a>
            </li>
        {{/each}}
        </ul>
    </script>
    
    <script type="text/x-handlebars" data-template-name='project-view'>
        <h2>Showing project <i>{{name}}</i></h2>
        {{outlet}}
    </script>
    
    <script type="text/x-handlebars" data-template-name='info-view'>
        {{description}}
    </script>
    

    JSFiddle @ http://jsfiddle.net/MikeAski/fRea6/