How to persist hasMany association in a single Ember.js form using Ember Data & Rails?

♀尐吖头ヾ 提交于 2019-12-04 13:21:35

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!