0
votes

I'm building an Ember app which uses quite a few components. I'm also using Bootstrap. I've got a layout with tabs, and inside the second tab (which is hidden by default), the component (which contains a list of models which have a hasMany relationship with the main model) won't render.

I think I tracked this down to Ember Data resolving after the view is rendered, because if I click on another model of the list, these relations will show up.

Some info and details:

I have two main models:

  • Image
  • Crop

An image can have many crops.

I have an Images/Index controller which has this function:

loadCrops: function() {
  var self = this;
  this.get('selectedImage').get('crops').then(function(crops) {
    self.set('selectedImageCrops', crops);
  });
}.on('model.isFulfilled')

I added this method because I tried to manually resolve the relationship and get the crops for the image loaded in a variable but I had no luck with this. I'm passing the variables like this:

{{image-crops image=selectedImage crops=selectedImageCrops}}

This is my Index route:

export default Ember.Route.extend({
  model: function () {
    return this.store.find('image');
  },
  setupController: function(controller, model, request) {
    controller.set('model', model);
  }
});

If anyone needs more details please, ask for them. Thank you all!

3
any reason to not use the setupController hook in your Images/Index route?Kori John Roys
oh, I'm using it to load the array of images. I added it to the questionpmerino
Ok, by default setupController already does what you're doing, so you can delete it ("The default setupController hook sets the model property of the associated controller to the route handler's model." -ember docs: guides.emberjs.com/v1.10.0/routing/setting-up-a-controller). How are you loading selectedImage?Kori John Roys
@KoriJohnRoys selectedImage: function() { return this.get('model').content[this.get('selectedIndex')]; }.property("selectedIndex"). selectedIndex just defaults to 0, and it stores the current selected index in a listpmerino
ok that begs the question: where are you setting selectedIndex?Kori John Roys

3 Answers

1
votes

When you use function() {}.on() you are telling Ember to execute that function when an event occurs. model.isFulfilled isn't an event though but a property so you need to observe it instead and do a quick check within the method that it really has been fullfilled (so it won't trigger if the promise is restarted for example)

loadCrops: function() {
  if(!this.get('model.isFulfilled')) {
    return;
  }
  var self = this;
  this.get('selectedImage').get('crops').then(function(crops) {
    self.set('selectedImageCrops', crops);
  });
}.observes('model.isFulfilled')

Also as a side note I would suggest that you use an ES6 arrow function (which retains the outer this) instead of using var self = this, it's make the code a bit nicer.

loadCrops: function() {
  if(!this.get('model.isFulfilled')) {
    return;
  }

  this.get('selectedImage').get('crops').then((crops) => {
    this.set('selectedImageCrops', crops);
  });
}.observes('model.isFulfilled')
0
votes

Try changing to a computed property:

selectedImageCrops: function() {
  return this.get('selectedImage.crops');
}.property('selectedImage')
0
votes

What I did in the end was moving the code to load the crops to the route's model, and returning an Ember.RSVP.hash with both the images and the crops, and then assigning it to the controller:

export default Ember.Route.extend({
  /**
   * Returns an Image instances array
   *
   * @method model
   * @return {Ember.RSVP.hash} Images & Crops
   */
  model: function () {
    return Ember.RSVP.hash({
      images: this.store.find('image'),
      crops: this.store.find('crop')
    });
  },
  /**
   * Takes care of setting the needed variables in the controller
   *
   * @method setupController
   */
  setupController: function(controller, model, request) {
    controller.set('model', model.images);
    controller.set('crops', model.crops);
  }
});

Then I added a helper function in the controller to get the current image's crops:

selectedImageCrops: function() {
  return this.get('crops').filter((obj) => {
    return obj.image === this.get('selectedImage');
  })[0];
}.property("selectedImage")

Thanks to @Karl-Johan Sjögren for the tip on the arrow function!