问题
I'm having trouble determining the correct way to persist a hasMany association with a single form using Ember.js, Ember Data and Rails. A Client hasMany Projects. I have a new project form that has two fields: project name and client name. http://cl.ly/image/3z0P0R3M1t2u
I've tried to keep my create logic within the Ember.js ClientsController & ProjectsController, but will I need to move some of that to the submit action on my ProjectsNewView?
Update: I've updated my code after finding this issue. I'm getting closer, but the Rails ProjectsController is still not receiving the associated client_id. It is not a part of the params that the controller receives. It still feels like I'm probably not going about this the best way.
Models:
Rails
class Client < ActiveRecord::Base
attr_accessible :name
has_many :projects
accepts_nested_attributes_for :projects
validates :name, :presence => true
end
class Project < ActiveRecord::Base
attr_accessible :name, :client_id
belongs_to :client
validates :name, :presence => true
validates :client, :presence => true
end
Ember.js
App.Client = DS.Model.extend
name: DS.attr('string')
projects: DS.hasMany('App.Project', { embedded: true })
validate: ->
if @get('name') is null or @get('name') is ''
'Client requires a name.'
App.Project = DS.Model.extend
name: DS.attr('string')
client: DS.belongsTo('App.Client')
validate: ->
if @get('name') is `undefined` or @get('name') is null or @get('name') is ''
return 'Projects require a name.'
if @get('client') is `undefined` or @get('client') is null or @get('client') is ''
'Projects require a client.'
Controllers:
Rails
class Api::ClientsController < Api::BaseController
def create
@client = Client.find_or_create_by_name(params[:client][:name])
respond_to do |format|
if @client.save
format.json { render json: @client, status: :create }
else
format.json { render json: @client.errors, status: :unprocessable_entry }
end
end
end
end
class Api::ProjectsController < Api::BaseController
def create
@project = Project.new(params[:project])
respond_to do |format|
if @project.save
format.json { render json: @project, status: :created, location: @project }
else
format.json { render json: @project.errors, status: :unprocessable_entry }
end
end
end
end
Ember.js
App.ClientsController = Em.ArrayController.extend
createClient: (data) ->
@transaction = App.store.transaction()
client = @transaction.createRecord(App.Client, data)
project_data = data.projects_attributes[0]
client.get('projects').createRecord(project_data)
validation_errors = client.validate()
if validation_errors
App.displayError validation_errors
client.destroy()
else
@transaction.commit()
App.ProjectsController = Em.ArrayController.extend
createProject: (data) ->
@transaction = App.store.transaction()
project = @transaction.createRecord(App.Project, data)
validation_errors = project.validate()
if validation_errors
App.displayError validation_errors
project.destroy()
else
@transaction.commit()
App.get('router').transitionTo('projects')
Views:
Ember.js
App.ProjectsNewView = Em.View.extend
classNames: ['form row']
tagName: 'form'
templateName: 'projects/new'
init: ->
@_super()
submit: (event) ->
event.preventDefault()
client = {}
client.name = @get('client')
project = {}
project.name = @get('name')
client.projects_attributes = []
client.projects_attributes.push project
App.router.clientsController.createClient(client)
回答1:
@David i am no expert but i was looking at your question again and i think to get the associated client_id, you will have to call toJSON method on the RestAdapter and pass it:
{associations: true}
The links below explain how to do that:
https://stackoverflow.com/questions/10184213/is-there-a-way-to-post-embedded-objects-back-to-the-api-with-ember-data
Ember-data embedded objects stored as separate objects
Emberjs - unable to query for embedded model or association
Update
Before i answer the question of where in the code to use toJSON, i want to go one step back to something i just observed with the design of your code.
You are trying to create a parentRecord within child record because Client is the parent while project is the child. If you observe well, issue-115 that you linked to, categorically says that the parentRecord store would be used to create the record. Also if you examine the test in evenutual pull-request that was merged in respect to issue-115, you will see it again stated as Create a child record within the parentRecord.
But you are doing the reverse, that is creating parentRecord within child record. You need to reverse that.
Also, on the rails side, in the Client Model, you called accepts_nested_attributes_for :projects, which just like emberjs does only allows you to create project records from Client and not vice-versa. All the examples of how to use accepts_nested_attributes_for in the rails guide, railscasts and this on nested attributes, show the fields for the child model created from the forms in the parent model.
So both rails and emberjs are in this use case to create parentRecord from child record. This could be why you having the client-id issue. So reverse your design and try it again. If it still doesn't send the client-Id. Then you should called toJSON on the Client Model which is the parent association as shown in the first link from the initial links i posted recommending you call {associations: true} on toJson in the restAdapter.
App.Client
##
toJSON: (options={}) ->
options['associations'] = true
@_super(options)
I hope this helps. Kindly comment back on what worked or did not work, so that others experiencing similar issues, who find this post can know where to start from.
Finally, it wouldn't hurt to create your rails api with active_model_serializer gem, which is ember-core team's recommended gem for creating rails api for ember-data and the RestAdapter. That way you can sideload and embed associations both from the ember side and the rails side.
来源:https://stackoverflow.com/questions/12439165/how-to-persist-hasmany-association-in-a-single-ember-js-form-using-ember-data