What's the proper way to access parameters from within Ember.Route. setupController?
Asked Answered
M

2

18

Ember.Route.model has access to the params variable, but Ember.Route.setupController does not. This is troublesome for me, because my path has multiple dynamic segments, and I need to use all of them in my template.

Specifically, my path looks like this: /project/:project_id/task/:task_id. Note that a task can belong to many projects, not just one. Therefore we can't tell what project we're looking at just be looking at the task itself: we have to use the project ID found in the URL. Here's how I'm doing it currently:

App.TaskRoute = Ember.Route.extend({

  // This works just fine:
  serialize: function(model) {
    return {
      task_id: model.get('id'),
      project_id: this.modelFor('project').get('id')
    };
  },

  model: function(params) {
    this.set('parentProjectId', params.project_id);

    return App.Task.find(params.task_id);
  },

  setupController: function(controller, model) {
    var parentProject = this.modelFor('project') ?
                        this.modelFor('project') :
                        App.Project.find(this.get('parentProjectId'));
    controller.set('parentProject', parentProject);

    controller.set('content', model);
  }
});

Maybe I'm being paranoid, this just feels hacky. If the route was meant to have access to the parameters, then it would already have a params property attached to it. Is there a better way?

EDIT: I made some update to the code above. Also, my routes look like this:

App.Router.map(function() {
  this.resource('project', { path: '/project/:project_id' });
  this.resource('task', { path: 'project/:project_id/task/:task_id' });
});
Mitosis answered 19/3, 2013 at 22:51 Comment(0)
P
11

You have no access to these params in the setupController hook. The model hook has access to a params object, because it is just called, when your app is entered via URL.

Your code looks quite fine, it you really know, that you want to do it this way. What does feel hacky to you about it? When looking at this code, i am asking myself why you did not split the logic of your Route into a ProjectRoute and a subordinated TaskRoute. Wouldn't that work for you?

Update: Response to your changes

Nesting resources is likely the key to success in your case:

App.Router.map(function() {
  this.resource('project', { path: '/project/:project_id' }, function(){
    this.resource('task', { path: '/task/:task_id' });
  });
});

Since the TaskRoute is nested not you have to rename it to ProjectTaskRoute:

App.ProjectTaskRoute = Ember.Route.extend({
...
});

This should enable you to remove the parentProjectId property from the Route.

Punctilio answered 19/3, 2013 at 22:59 Comment(4)
Ah, I wasn't aware that the model method is only called on first load, but it explains a lot of quirkiness I've been seeing. For example, my code above doesn't work if I open /project/1/tasks/2 directly, because model isn't called, so the parentProjectId attribute isn't set. In that case, I have to do var parentProject = this.modelFor('project') inside setupController. I do have a separate ProjectRoute. Opening /project/1 will show tasks for a project. Clicking a task will open /project/1/tasks/1. The task view needs a link back to the project, so I'm pulling the project from the URL.Mitosis
So it works now? My idea was also to use this.modelFor("project"). When you enter your app via /project/1/tasks/2, the ProjectRoute should be hit first and its model hook. Afterwards you can use modelFor to access it. It feels only a little bit redundant to set the parentProject in your controller, when you use ember-data to model your data. Shouldn't exist that property on your TaskModel?Punctilio
A task can belong to multiple projects (think of projects like tags), so instead of having a "belongs to project" association it's a "has many projects" association. Thus, I can't tell which project I'm inside of (and thus where the back button should link to) just by looking at the task. I have to check the URL. Anyway, I'll try the modelFor thing. I think I have my routes set up incorrectly, so going to /project/1/task/2 is hitting TaskRoute first. I think I need to nest my task routes inside my project routes? I'll update my question to show what my routes look like.Mitosis
Yes, your routes should definitely be nested! Then the ProjectRoute would be hit first. I will comment on your update, when i have time.Punctilio
B
2

Since Ember 1.8, the Route class has a paramsFor function:

import Route from '@ember/routing/route';

export default Route.extend({
    setupController(controller) {
        this._super(...arguments);
        const params = this.paramsFor('name.of.your.route')
    }
});
Blanchblancha answered 17/5, 2018 at 15:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.