2

I'm a Rails guy trying to become a Rails API server + Backbone front end guy, and I have a strong feeling I'm not doing something right with respect to creating new instances of a model inside a collection contained in another object.

In all the below I'm using backbone-relational. FWIW I'm also using Marionette but I don't think that matters much here.

To get specific, say that I have an Engine model, and that Engines can have many Gears. I am converting my Engine edit page in the old Rails code to a little Backbone app. My new Backbone Engine edit page looks like this:

Engine edit page mockup

When I click the "Add New Gear" button, I would ideally like the outcome to be (1) that the server creates (persists) a new blank Gear object connected to the Engine and (2) that the a new backbone Gear instance gets added that is synched to the new server state.

In a purely Rails world, I could POST to a URL like:

api/engines/42/gears

and that would create the new Gear instance on the server side and I would redirect to some page. I just don't get how to do that on the client side and have everything be synched up in the end.

Here are the Backbone (Relational) models for Engine, Gear, and the Gears collection:

class Engine extends Backbone.RelationalModel
  urlRoot: '/api/engines'

  relations: [
    {
      type: Backbone.HasMany,
      key: 'gears',
      relatedModel: 'Gear'
      collectionType: 'Gears',
      reverseRelation: {
        key: 'engine',
        includeInJSON: false
      }
    }
  ]

Engine.setup()   // here b/c I'm using coffeescript

Here's the Gears collection:

class Gears extends Backbone.Collection
  model: Gear

And here's the Gear model:

class Gear extends Backbone.RelationalModel
  urlRoot: '/api/gears'

Gear.setup()    // here b/c I'm using coffeescript

I can create a new backbone Gear object with newGear = new Gear(), but how do I connect it into the Engine's list? If I say myEngine.get('gears').add(newGear), what am I supposed to save?

I'm hoping I can somehow just save (POST) the new gear without calling save() on the Engine instance. Trying to update the engine instance (myEngine.save() after the above) doesn't really seem to do the trick anyway because the new Gear instance doesn't yet have an ID so the Engine update call freaks out.

If anyone can steer me in the right direction I'd appreciate it!

And sorry for anyone who ended up here looking for information about Rails Engines... bad example name choice :-)

JP Slavinsky
  • 345
  • 3
  • 9

1 Answers1

1

I was able to figure this out. Writing a SO question really helps to focus the mind :-)

The final code to add the Gear to the Engine is pretty simple:

gear = new Gear()
myEngine.get('gears').create(gear)

I had tried this before (actually I had the second line split as an add(...) followed by a save() call) but it wasn't working. There were multiple reasons for this, the biggest of which was my setup of the HasMany relation. Here's the updated relation for the Engine class:

class Engine extends Backbone.RelationalModel
  urlRoot: '/api/engines'

  relations: [
    {
      type: Backbone.HasMany,
      key: 'gears',
      relatedModel: 'Gear'
      collectionType: 'Gears',
      reverseRelation: {
        key: 'engine',
        keySource: 'engine_id',   # wasn't here before
        includeInJSON: 'id'       # used to be false
      }
    }
  ]

keySource and includeInJSON together let me specify how I want the engine represented in the gear's JSON output. Earlier, when I just had the default value for includeInJSON, true, I was getting the entire engine serialized in gear's JSON output, which I didn't want. So I changed it to false. But in the POST that eventually worked to send the new Gear object to the server to be created, I really needed the engine foreign key so that the Rails server could hook up the new model correctly. If I switched includeInJSON to 'id' so that the serialization had {engine: <id value>} that was better (except that for Rails I really wanted {engine_id: <id value>}. BUT, with this setup whenever I said myGear.get('engine') I got the ID of the engine, when I really wanted the whole model. The discovery of keySource lets me send {engine_id: <id value>} out to the API server while still letting my get the engine model through myGear.get('engine').

Then in my Rails API I can take the engine_id and create the appropriate blank Gear, which then gets spit back to the client as JSON, which then updates the view.

If anyone else has any insights into this whole situation, I'd of course still be happy to hear them.

JP Slavinsky
  • 345
  • 3
  • 9