23

I'm using Rails, backbone.js (learning this now). Let's say you have two models, Car and Engine.

var Car = Backbone.Model.extend({
  initialize: function() {
    if(this.get('engine') != undefined) this.engine = new Engine(this.get('engine'));
  }
}

var redCar = new Car({
      'color': 'red',
      // The controller nests the model
      'engine': {
         'horsepower': '350'
       }
    });


redCar.save()

What is the right way to send engine_attributes to the controller? (Car accepts_nested_attributes_for :engine, so it expects engine_attributes.) Do I override the Backbone sync()? Is there a better convention to follow for nested models?

Maybe I should not be returning nested models from the controller, or returning engine_attributes instead of engine?

On a side note, I am using the Rails respond_with(@car, :include => :engine) (same as @car.to_json(:include => :engine). The fact that this api nests the engine attributes under engine but the model expects engine_attributes seems contradictory - I've never been sure how to reconcile this.

Alex Neth
  • 3,326
  • 2
  • 26
  • 36

4 Answers4

33

I would suggest to override toJSON on the backbone model.

toJSON: function(){

  json = {car : this.attributes};
  return _.extend(json, {engine_attributes: this.get("engine").toJSON());

}

toJSON is called within the sync method just before sending data to the backend.

Julien
  • 9,216
  • 4
  • 37
  • 38
  • 1
    Awesome, id suggest making your rails controller deal with backbones format, but this is much MUCH cleaner – Vlad Gurovich Mar 10 '11 at 01:41
  • I agree - I wish there was an option to do this in backbone. – Alex Neth Mar 22 '11 at 02:04
  • Awesome, exactly what I was looking for. – c3rin Jul 08 '11 at 17:35
  • 15
    this.attributes should be cloned, or you'll soon be in a world of hurt :) Should be: _.clone(this.attributes) – Emil Stenström Aug 17 '11 at 13:44
  • Is this also the recommended solution for adding read-only props to the model? (e.g. special formats/calculated properties to be used in templates) – Dan Sep 16 '11 at 11:10
  • For templates? No I wouldn't use that. I personnally create accessors to compute/format the data. You could think of full fledge formatters but it can be difficult to manage for small projects. – Julien Sep 17 '11 at 02:29
  • As Emil Stenström pointed out, you should clone it first or use: _.extend({},json, some_attributes); – Cu7l4ss Nov 20 '12 at 12:45
  • I think we must be careful with this approach, because this method is also used in the views, i.e. adding namespace will affect the way you're accessing attributes there. – wik Nov 28 '14 at 20:33
  • I've added toJSON function to the backbone model where I've the nested attribute, but it is not getting invoked. – user1801879 Dec 19 '15 at 05:23
2

I found this gem! Maybe help you.

http://blog.dtmtec.com.br/blog/2013/05/power-up-your-backbone-models-with-nested-attributes/

2

Very helpful. I was working with a similar situation, and this example did not work for me at first. In my example I have a has_many / belongs_to relation. For example a car has_many :tires.

The problem I encountered was that the tires_attributes needed to be nested INSIDE of the car json, not adjacent to it. I ended up with something like this:

toJSON: function(){

  json = {car : this.attributes};
  json.car.tires_attributes = this.get('tires').toJSON();
  return json;

}

Hope this helps.

StereoSteve
  • 375
  • 2
  • 8
  • there are two more options: 1) `wrap_parameters` which is a bit weird; 2) define attributes on the Car model as attr_accessible, make sure to include `tires_attributes` into the list, yes exactly!; – wik Nov 28 '14 at 21:23
0

I believe we've to do something with (or around) Backbone sync or with rails backend, because the problem in communication between two. Overriding toJSON() method might lead to unexpected results, because this is general purpose method and others parts of application might rely on, for example views.

Possibly quick solution:

redCar.save({}, {
    contentType: 'application/json',
    data: JSON.stringify({car: redCar.toJSON(), engines_attributes: redCar.get('engines').toJSON()})
});
wik
  • 2,462
  • 1
  • 24
  • 29