How to correctly access a parent controllers data (model) in nested resources?

2.1k views Asked by At

I have a little EmberJS app to test things out hot to do nested resources. Sometimes accessing a parent routes/controllers data work, other times not. Most likely this is due to a oversight on my part with how EmberJS does its magic.

Here is the app:

window.App = Ember.Application.create();

  App.Router.map(function() {
    this.resource('items', function() {
      this.resource('item', {path: ':item_id'}, function() {
        this.resource('subitems');
      });
    });
  });

  App.ApplicationController = Ember.Controller.extend({
    model: {
      items: [
        {
          id: 1,
          name: 'One',
          subitems: [
            {
              id: 1,
              name: 'One One'
            }, {
              id: 2,
              name: 'One Two'
            }
          ]
        }, {
          id: 2,
          name: 'Two',
          subitems: [
            {
              id: 3,
              name: 'Two One'
            }, {
              id: 4,
              name: 'Two Two'
            }
          ]
        }
      ]
    }
  });

  App.ItemsRoute = Ember.Route.extend({
    model: function() {
      return this.controllerFor('Application').get('model.items')
    }
  });

  App.ItemRoute = Ember.Route.extend({
    model: function(params) {
      var items = this.controllerFor('Items').get('model')
      var item  = items.filterBy('id', parseInt(params.item_id))[0]
      return item
    }
  });

  App.SubitemsRoute = Ember.Route.extend({
    model: function(params) {
      var item = this.controllerFor('Item').get('model')
      var subitems = item.get('subitems')
      return subitems
    }
  });

http://jsfiddle.net/maxigs/cCawE/

Here are my questions:

Navigating to items/1/subitems throws an error:

Error while loading route: TypeError {} ember.js:382
Uncaught TypeError: Cannot call method 'get' of undefined test:67

Which i don't really get, since apparently the ItemController loads its data correctly (it shows up) and the same construct works for the ItemsRoute as well to get its data.

Since i don't have access to the parents routes params (item_id) i have no other way of re-fetching the data, even though directly accessing the data from ApplicationController works fine.

Why do i have define the root data in a controller not route?

Moving the model definition from ApplicationController to ApplicationRoute, does not work. Conceptually, as far as i understand it, however this should even be the correct way to do it, since everywhere else i define the mode data for the controller int he route.

Or should the whole thing be better done via the controllers needs-api? As far as i understood the needs are more for only accessing extra data within the controller (or its view) but the routers job is to provide the model.

2

There are 2 answers

0
maxigs On BEST ANSWER

After some more fiddling around here is a working version of the example in the question:

window.App = Ember.Application.create();

App.Router.map(function() {
    this.resource('items', function() {
        this.resource('item', {path: ':item_id'}, function() {
            this.resource('subitems');
        });
    });
});

App.ApplicationRoute = Ember.Route.extend({
    model: function() {
        return Ember.Object.create({
            items: [
                Ember.Object.create({
                    id: 1,
                    name: 'One',
                    subitems: [
                        {
                            id: 1,
                            name: 'One One'
                        }, {
                            id: 2,
                            name: 'One Two'
                        }
                    ]
                }), Ember.Object.create({
                    id: 2,
                    name: 'Two',
                    subitems: [
                        {
                            id: 3,
                            name: 'Two One'
                        }, {
                            id: 4,
                            name: 'Two Two'
                        }
                    ]
                })
            ]
        })
    }
});

App.ItemsRoute = Ember.Route.extend({
    model: function() {
         return this.modelFor('application').get('items')
    }
});

App.ItemRoute = Ember.Route.extend({
    model: function(params) {
        return this.modelFor('items').findProperty('id', parseInt(params.item_id))
    }
});

App.SubitemsRoute = Ember.Route.extend({
    model: function(params) {
        return this.modelFor('item').get('subitems')
    }
});

http://jsfiddle.net/maxigs/cCawE/6/ and deep link into subitems (that did not work previously) http://fiddle.jshell.net/maxigs/cCawE/6/show/#/items/2/subitems

What changed:

  1. root-model data moved into ApplicationRoute
  2. root-model moved into an ember object, and sub-objects are also their own ember objects (so calling get('subitems') and other ember magic works)
  3. changed all the controllerFor('xxx').get('model') into modelFor('xxx') (lower case!), which probably has no effect other than consistency

I'm still not sure if this is now "the ember way" of doing what i have here but its consistent and works completely as wanted.

2
dierbro On

1. Navigating to items/1/subitems throws an error:

Your model is just a javascript object so there isn't a method get to fetch the data. You can access the subitems by just executing item.subitems.

Also the argument of controllerFor() should be lower case.

For instance:

this.controllerFor('application')

2. Why do i have define the root data in a controller not route?

You can set the model from the route in the setupController method.

App.ApplicationRoute = Ember.Route.extend({
  setupController: function(controller) {
    controller.set('model', { ... });
  }
});

http://jsfiddle.net/Y9kZP/