Many to Many Relationships with Ember, ember-data and Rails

ember-data, ember-rails, ember.js, has-many-through, many-to-many

Solution

Despite the fact that it's possible to set up matching `hasMany` relationships, Ember Data doesn't actually support many to many relationships yet (see this issue). What you can do for now is decompose the relationship using a membership model.

App.User = DS.Model.extend
  rosterMemberships: DS.hasMany 'App.RosterMembership'      

App.RosterMembership = DS.Model.extend
  user: DS.belongsTo 'App.User'
  roster: DS.belongsTo 'App.Roster'

App.Roster = DS.Model.extend
  rosterMemberships: DS.hasMany 'App.RosterMembership'

Now you can use `createRecord()` and `deleteRecord()` with the membership model to add and delete relationships.

Unfortunately, in this example, it's not so easy to bind to the collection of rosters for a particular user. One work-around is as follows:

App.User = DS.Model.extend
  rosterMemberships: DS.hasMany 'App.RosterMembership'
  rosters: ( ->
    @get('rosterMemberships').getEach('user')
  ).property 'rosterMemberships.@each.relationshipsLoaded'

App.RosterMembership = DS.Model.extend
  user: DS.belongsTo 'App.User'
  roster: DS.belongsTo 'App.Roster'
  relationshipsLoaded: ( ->
    @get('user.isLoaded') and @get('roster.isLoaded')
  ).property 'user.isLoaded', 'roster.isLoaded'

If you bind to `user.rosters`, then your template should update when relationships are created or destroyed.

Problem

I am having an issue with trying to save many to many relationships in Ember.js using ember-data and rails. The association works fine on the ember side of things, however when I try to commit the transaction it will not include the list of new associations when submitting to rails. Any help would be much appreciated, I've been tearing out my hair trying to find an example application that does something this simple on github but I can't seem to find one. Here is a dumbed down version of my Ember code: ``` App.Store = DS.Store.extend revision: 11 adapter: DS.RESTAdapter.create url: '/api' App.Router.map -> @resource 'rosters' @resource 'users' App.User = DS.Model.extend rosters: DS.hasMany 'App.Roster' App.Roster = DS.Model.extend users: DS.hasMany 'App.User' App.RostersEditRoute = Ember.Route.extend setupController: (controller, model) -> controller.set 'users_list', App.User.find() App.RostersEditView = Ember.View.extend userCheckbox: Em.Checkbox.extend checkedObserver: ( -> users = @get 'roster.users' if @get 'checked' users.addObject @get 'user' else users.removeObject @get 'user' @get('controller.store').commit() ).observes 'checked' ``` Edit form: ``` each user in users_list label.checkbox view view.userCheckbox rosterBinding="current_roster" userBinding="user" | #{user.full_name} ``` Rails Backend (Using Inherited Resources and Active Model Serializer gems): ``` class User < ActiveRecord::Base has_many :rosters_users has_many :rosters, through: :rosters_users accepts_nested_attributes_for :rosters end class RostersUser < ActiveRecord::Base belongs_to :user belongs_to :roster end class Roster < ActiveRecord::Base has_many :rosters_users has_many :users, through: :rosters_users accepts_nested_attributes_for :users end module Api class RostersController < BaseController end end module Api class UsersController < BaseController end end module Api class BaseController < ActionController::Base inherit_resources respond_to :json before_filter :default_json protected # Force JSON def default_json request.format = :json if params[:format].nil? end end end class UserSerializer < BaseSerializer has_many :rosters, embed: :ids end class RosterSerializer < BaseSerializer has_many :users, embed: :ids end ``` So like I said, the association works fine on Ember but rails doesn't seem to be getting the new association data. When I check out the XHR tab in the web inspector, I see it only sent this: Request Payload {"roster":{"created_at":"Thu, 21 Mar 2013 23:16:02 GMT","team_id":"1"}} And I am returned with a 204 no content error because nothing changed.

Original source