How to deal with additional data

I had this problem a couple of times now. I have Models set get not all data when they are loaded in the first place. Lets say I load a bunch of products, this products have user comments but I don’t want load all the comments when I just need to show a list of products. But when the user goes to single product page I need to load this data. Whats the common way to load these additional data to an ember model?

Using sideloading. If you’re using Ember Data you can check out this sideloaded relationships guide.

It requires your server to support sideloading though. Checkout my blog post under “Make it possible to include relationships” for tips.

But sideload means I have to load the data in the same request. But I can’t or don’t want to load the data in the first place. Think about a the list of products. I don’t want to load all comments for all products. But on the products place I want to load the data. But there the product model still exist so I can’t load them easily in the ProductRoute.model method.

It sounds like you want to partially load models. There was some discussion about this with a couple of answers here Loading partial models then filling them with Ember-data

@lookingsideways, thanks for the link, but both solution seems wrong for me or maybe I don’t understand them. What I have in mind is the following: the only one that could know that there are some data is missing in the model is the controller, or maybe the route, so there must be a way where the controller/route check if the model has all data thats is needed and if not it loads the data additional and add them to the model.

Are you using Ember Data? Or how are you handling your API requests?

Yes I’m using EmberData.

If you have a hasMany('App.Comment') inside App.Product, can’t you just let the app make two requests when visiting /products/:product_id? One for the product (e.g. /api/products/1234), and one for all the comments (e.g. /api/comments?product_id=1234.

I believe that product.get('comments') should automatically do that for you.

@seilund Ok this is what my route looks like after some tries:

ProductRoute = Ember.Route.extend({
  model: function(params) {
    return  App.Product.find(params.product_id)
  },


  setupController: function(controller, model) {
    controller.set('content', model);

    var productcomments = model.get('product_comments');
    console.log(productcomments);
    if (!productcomments.loadingRecordsCount) {// unfortunately this is always `0` 
      App.ProductComment.find({id: model.id}).then(function(result) {
        productcomments.pushObjects(result)
      });
    }
  }
});

As I’m new to ember I’m still unsure if this is the right way to go.

If your product route contains a dynamic segment named :product_id you actually don’t need the model hook at all. Ember will automatically detect that you have a model called App.Product and find the record for you. Read more in the guides

Are your models something defined like this?

App.Product = DS.Model.extend({
  comments: DS.hasMany('App.Comment')
});

App.Comment = DS.Model.extend({
  product: DS.belongsTo('App.Product'),
  message: DS.attr('string')
});

In that case you should be able to just loop over comments in your product.hbs template:

{{#each comments}}
  {{message}}<br/>
{{/each}}

When inside the product.hbs template the context is the current product. That’s why you can access comments directly using comments, which is a computed property of the hasMany relationship.

Let me see a full jsbin or something if you have more questions :slight_smile:

var productcomments = model.get('product_comments');

If the product is already in the store without the comments then this will return an empty array without hitting the server, you need to run the find below in order to load them:

  App.ProductComment.find({id: model.id}).then(function(result) {
    productcomments.pushObjects(result)
  });

Should this be App.ProductComment.find({product_id: model.id}) instead?

@lookingsideways yes thats the point of the whole story, I have a model that is still in the store but without the comments. So my last solution works, and using product_id seems more accurate. I also found a way to check if the comments are loaded, as productcomments.loadingRecordsCount was always 0.

setupController: function(controller, model) {
    controller.set('content', model);

    var productcomments = model.get('product_comments');
    if (!productcomments.content.length) {
      App.ProductComment.find({product_id: model.id}).then(function(result) {
        productcomments.pushObjects(result)
      });
    }
  }

So my last question is, whats the best place to load the data, while loading it in the setupController method works, loading additional data to the model has not much to do with settings up the controller.

So here is my final solution:

App.ProductRoute = Ember.Route.extend({
  afterModel: function(model) {
    model.ensureComments();
  }
});

Product = DS.Model.extend({
  page_title: DS.attr('string'),
  image: DS.attr('string'),
  shop: DS.belongsTo('App.Shop'),
  user: DS.belongsTo('App.User'),
  product_comments: DS.hasMany('App.ProductComment'),
  ensureComments: function() {
    var productcomments = this.get('product_comments');
    if (!productcomments.content.length) {
      App.ProductComment.find({product_id: this.id}).then(function(result) {
        productcomments.pushObjects(result)
      });
    }
  }
});
1 Like