4
votes

I have a setupController and model hook in my route. The model.find(param) holds a ajax call to the rails backend.

What happens is that my model hook fires and calls to the backend. But before the backend answers and the success callback actually comes back with the model, the setupController hook fires with a model that is still undefined.

End result is that while the model itself is being updated through the binding system, the additional setup that needs to take place in the setupController is not working. I have the feeling that I'm somehow setting the content of the model wrong, but can't figure it out.

This only happens by the way if I use the back and forward button to navigate. If I go in the route through an #linkTo with the object specified everything works as expected.

The code I have is below, if somebody has an idea that would be great.

App.ProjectsEditRoute = Ember.Route.extend({
  model: function(params) {
    return App.Project.find(params.project_id);
  },

  setupController: function(controller, model) {
    this.controllerFor('activedataset.index').set('content', App.ActiveDataSet.findAll(model.id));
  }
});

App.Project = Ember.Object.extend({
  id: '',
  name: ''
});

App.Project.reopenClass({
  findAll: function() {
    var result = Ember.ArrayProxy.create({content: []});
    var self = this;
    $.ajax({
      url: '/projects.json',
      type: 'GET',
      data: {'user_id': App.currentUser.id},
      success: function(data, textStatus, xhr) {
        result.set('content', data.projects);
      },
      error: function(xhr, textStatus, errorThrown) {
        alert('error');
      }
    });

    return result;
  },

  find: function(project_id) {
    var result = Ember.Object.create({content: null});
    var self = this;
    $.ajax({
      url: '/projects/' + project_id + '.json',
      type: 'GET',
      success: function(data, textStatus, xhr) {
        result.setProperties(data.project);
      },
      error: function(xhr, textStatus, errorThrown) {
        alert('not found');
      }
    });

    return result;
  }
});

Edit after solution provided by Mike:

App.ProjectsEditRoute = Ember.Route.extend({
  model: function(params) {
    var record = App.Project.find(params.project_id);
    var promise = Ember.Deferred.create();
    record.addObserver('isLoaded', function() {
      promise.resolve(record);
    });

    return promise;
  },

  setupController: function(controller, model) {
    this.controllerFor('activedataset.index').set('content', App.ActiveDataSet.findAll(model.id));
  }
});

App.Project.reopenClass({
  find: function(project_id) {
    var result = Ember.Object.create({content: null, isLoaded: false});
    var self = this;
    $.ajax({
      url: '/projects/' + project_id + '.json',
      type: 'GET',
      success: function(data, textStatus, xhr) {
        result.setProperties(data.project);
        result.set('isLoaded', true);
      },
      error: function(xhr, textStatus, errorThrown) {
        alert('not found');
      }
    });

    return result;
  }
});
2

2 Answers

4
votes

I have the feeling that I'm somehow setting the content of the model wrong, but can't figure it out.

What your doing is not wrong per-se. If you need ember router to wait for model ajax to complete before moving on to setupController hook your model's find() should return a promise. If a route's model hook returns a promise, ember router will wait for the promise to be resolved before moving on.

By default ember-data's model objects return a promise when you call find({}). It should be easy to do the same for your model, see this Ember initializing route model with query for an example of the deferred patter.

This only happens by the way if I use the back and forward button to navigate. If I go in the route through an #linkTo with the object specified everything works as expected.

Makes sense. When you use a #linkTo the data is already loaded.

0
votes

You probably should use this.store.find('project') instead of rolling your own App.Project.find().

DS uses promises, and therefore calling code will wait for fetched object(s).

What is somewhat undercommunicated in regards to rails and ember working together, is that you probably should use ActiveModel::Serializers in your rails controllers and the ams adapter in DS.Store for best and expected results.

App.Store = DS.Store.extend({
    // Override the default adapter with the `DS.ActiveModelAdapter` which
    // is built to work nicely with the ActiveModel::Serializers gem.
    adapter: '_ams'
});

My experience is that you are in for a world of pain if you start sidestepping Ember and/or DS. The hard part of using Ember is that you, arguably, have to understand every facet of it to be effective.