Accessing Parent Model from Child Route


#1

I have a child route, with a child model, in which I display data about that child model. I also want to be able to display a few other bits of information about the parent model. But this seems to be far more complicated than it ought to be.

The route for the child contains two dynamic segments - //parent/:parent_id/child/:child_id.

The child model is set by the link-to helper in the parent route. This seems the sensible, clean and logical way to set the child model. However, it bypasses the model hook in the route, which is the only place I seem to be able to access the parent model.

Ideally, I’d like to reopen the model in the route, and hash the parent model together with the child model. Alternatively, I’d like to be able to access the parent model from the child controller, in a similar way to modelFor in the route. Either of these would allow me to make use of the dynamic segments in the URL.

The only way I can see to be able to do this is to use query params, and just maintain the parent model throughout. I really don’t want to do this. Frankly, it would be ugly, and would needlessly complicate the design. Any better solutions?


#2

Actually I forgot why I did this but you can do this:


#3

Thanks @broerse.

Tried to get it all in one line, but couldnt. Went for this:

import Ember from 'ember';

export default Ember.Controller.extend({

    groupController: Ember.inject.controller('groups.group'),
    group: Ember.computed(function(){
        return this.get('groupController').get('model');
    }),
});

Still seems a shame not to be able to make use of those dynamic segments…


#4

@rjoxford you have a bug there. You have made a computed property without any dependent keys. The computed property will not update when the model changes. Make sure to read the guides section about the ember object model to get an idea of the basics.

You don’t even need to do that because you can just use a computed property macro like computed.reads

But if you want to put it in the model you can do that to. In the model hook you can just use modelFor and RSVP.hash to build a composite model made of the parent model and the child model. Using the controller injection is probably simpler as it doesn’t lead to forcing you to only use link-to or transitionTo in certain ways.


#5

Hi @alexspeller . Thanks for your time here.

I haven’t explained things very well. I’m looking to display a list of scores that a group of students get for different topics in a unit of study.

So the parent route is the group route, with a group model, which displays the units being studied. We can then click on a unit to go to the nested unit route. In the unit route, we can see the list of topics in that unit - the model in this case is the unit. We can also see the scores that each student in the group got for each topic in the unit.

The unit is a collection of topics, but any different group can work on the same unit, so I need to access the group model in order to be able to obtain its related students and their scores.

At the moment, I’m just using a component, passing it the topic and the group, and letting it find the scores for each student in the group.

Can you point me in the direction to read more about computed.reads? Please feel free to give this amateur some advice! Cheers!


#6

hey @rjoxford, not sure if this will be helpful to you or not, I will admit I didn’t read the above super carefully :grimacing: but in our app we use parent model in child routes like this (market is the parent route and there are 4 child routes per market):

// routes/<child-route>.js, router path is 'markets/:id/<child-route>

...
    model: function(params){
      var parentModel = this.modelFor('market');

      return Ember.RSVP.hash({

        // inherit the productGroup, expirations and types models from the parent 'market/:id' route instead of fetching them again
        productGroup: parentModel.productGroup,
        expirations: parentModel.expirations,
        types: parentModel.types,

        // request the child route's model from the API 
        childModels: this.store.query("things", {"product_group_id": parentModel.productGroup.id, ...})
      });
    },
...

Then you can access all the parent stuff in the child controller exactly as you could in the parent controller.

EDIT: this is basically what @alexspeller described above


#7

Thanks @dknutsen. No, it was a bit complicated and ugly to read!

This is what I wanted to do, and what I guessed would be “the Ember way”. The problem with this is that the model hook doesn’t get called if you navigate to the route using a link-to helper. How do you solve this problem?

I’m wondering whether its possible to ensure the model hook does get called? Perhaps a bit of a hack in beforeModel? But then that probably wouldn’t be a good idea…


#8

In your link-to, if you pass a model for the dynamic segment paramter, the model hook won’t get called. If you pass the id instead, the model hook will get called.


#9

I’m using the setupController hook in my child route:

export default Ember.Route.extend({
  model(params) {
    // fetch your child model
  },
  
  setupController(controller, model) {
    controller.set('parentModel', this.modelFor('parent'));
    this._super(controller, model)
  }
});

With this hook you can access the parent model from the child controller. The setupController hook will always be called even if you provide a model for the transition.


#10

Note: A route with a dynamic segment will always have its model hook called when it is entered via the URL. If the route is entered through a transition (e.g. when using the link-to Handlebars helper), and a model context is provided (second argument to link-to), then the hook is not executed. If an identifier (such as an id or slug) is provided instead then the model hook will be executed.

Game changer! Didn’t realise this… Thanks for pointing out. Its almost as if someone thought of this…!