Another hack to refill partially-loaded Models


#1

Since Ember doesn’t yet auto-reload models to fill in missing data, here’s my simple workaround.

Basically, I look to see if one of the missing fields is null, and if so, reload()

App.StoriesRoute = Ember.Route.extend({
  model: function() {
    return App.Story.find();
  }
});

App.StoryRoute = Ember.Route.extend({
  model: function(params) {
    var record = App.Story.find(params.story_id);
    var r = record;
    if(r.get('subtitle') == null) {
      r.reload();
    }
    return record;
  }
});

It’s less of a hack than having two models or changing things on the server. Ideally we could mark said model property as read-only too, e.g.

App.Story = DS.Model.extend({
  title: DS.attr('string'),
  subtitle: DS.attr('string').readOnly()
});

Anyone have any lower-level ideas?


#2

Funny, I implemented this earlier this week. I guess it’s a common problem.

The approach I took was to add a “fictitious” property on the server side via ActiveModel::Serializers. I called it isHeader to indicate that it’s an incomplete model (ie: just a header).

I have two serializer classes, one that returns a full model with isHeader false, and one that returns a subset of the data with isHeader true.

In setupController, I check if isHeader is false… and if it is I call reload.

I was originally basing it off a particular value like you are, but I was concerned that I couldn’t tell the difference between the field being set to null or empty, vs. not being provided due to a partial load.


#3

Here’s the approach I’m using. it’s similar to @suckerpunch’s, but the reload happens on ‘setupController’ and I’m careful to make sure that the record loading doesn’t delay rendering. Also, ‘reload’ didn’t work with a custom slug, so I do a manual find.

// Load the light version of all subjects on page load
App.ApplicationController = Em.Controller.extend({
  init: function() {
    return App.Subject.find();
  }
});

// Fetch all our previously loaded subjects
App.SubjectsRoute = Ember.Route.extend({
  model: function() {
    return App.Subject.all();
  }
});

App.SubjectRoute = Ember.Route.extend({
  // If we're loading the page directly to this route, do a normal find
  model: function(params) {
    return App.Subject.find(params.subject_id);
  },
  setupController: function(controller, model) {
    // Show what details we have for this subject (e.g. the title) immediately
    controller.set("model", model);

    // Load full details for the model and display them as soon as they arrive
    if (Em.isEmpty(model.get("text"))) {    
      App.Subject.find(model.get("id")).then(function(model) {
        return controller.set("model", model);
      });
    }
  }
});

#4

I solved the same problem recently in a similar way, by using a ‘partial’ attribute to indicate that model data is not complete. I then call reload() in the detail view if the model is partial, but also make sure to not overwrite complete models with partial data should the list data be fetched afterwards.

See my blog post for a full write-up.