Rails的accept_nested_attributes_for与f.fields_for和AJAX

我很好奇如何正确使用f.fields_forf.fields_for

意见/命令/ new.html.erb

 <%= form_for @order, html:{role: "form"} do |f| %> <%= f.submit "Don't push...", remote: true %> <%= f.text_field :invoice %> <%= f.text_field :ordered_on %> <%= f.text_field :delivered_on %> <table id='order_form'> <h3>Details</h3> <tbody> <%= render 'order_details/details', f: f %> </tbody> <%= link_to 'add line', new_order_detail_path(company_id: params[:company_id]), remote: true %> <%= link_to 'new box', new_box_path, remote: true %> </table> <% end %> 

视图/ ORDER_DETAILS / _details.html.erb

 <tr class='row0'> <%= f.fields_for :order_details, child_index: child_index do |d| %> <td><%= d.collection_select :box_id, @boxes, :id, :uid, {}, { name: "box_id", class: 'form-control'} %></td> <td><%= d.text_field :quantity, class: 'form-control' %></td> <td><%= d.text_field :box_price, class: 'form-control' %></td> <td><%= d.text_field :cb_price, class: 'form-control' %></td> <td><%= d.text_field :mould_fees, class: 'form-control' %></td> <td>$$$</td> <% end %> </tr> <tr class='box0'> <td colspan="6">→ <b><%= @b.uid %></b> | length: <%= @b.length %> | width: <%= @b.width %> | height: <%= @b.height %> | weight: <%= @b.weight %></td> </tr> 

controllers / orders_controller.rb (我很确定这是错误的…任何帮助在这里将不胜感激)

 def create @order = Order.create(params[:order]) if @order.save flash[:success] = "Order #{@order.invoice} added!" redirect_to current_user else render 'orders/new' end end 

车型/ order.rb

 class Order < ActiveRecord::Base attr_accessible ..., :order_details_attributes has_many :order_details accepts_nested_attributes_for :order_details end 

我已经能够获得fields_for的唯一方法是如果我实际上将fields_for作为fields_for Order.new.order_details.build 。 但是,这根本不构build嵌套的对象。 我需要使用f.fields_for命名来构buildOrder和OrderDetail。 不过,我只能build立一个。 这是我的下一个问题。

看看那里有button吗? 它将AJAX行插入到表单中。 如果你点击add line ,我会得到

 NameError in Order_details#new Showing D:/Dropbox/Apps/rails_projects/erbv2/app/views/order_details/new.js.erb where line #3 raised: undefined local variable or method `f' for #<#<Class:0x5cf0a18>:0x5cbd718> 

意见/命令/ add_detail.js.erb

 $('#order_form tr.total').before("<%= j render partial: 'orders/details', locals: {f: @f, child_index: @ci} %>") 

我不知道如何定义f …我检出了Rails的AJAX:我的部分需要一个FormBuilder实例和其他一些。

任何build议我应该如何处理? 使用我在这里的代码…我能够创build一个新的订单,与相关的order_details,但box_id没有保存,并且company_id没有保存。 我知道这是一种讨厌的,但我不知道还有什么要去的。

UPDATE

路线:

 resources :orders do collection { get :add_detail } end this is way better than having a separate resource for the details. I didn't think of this before! 

HTML表单:

 <%= form_for @order, company_id: params[:company_id], html:{role: "form"} do |f| %> f. ... <%= render partial: 'details', locals: { f: f } %> #first child <%= link_to 'add line', add_detail_orders_path(company_id: params[:company_id]), remote: true %> #add subsequent children <% end %> 

订单控制器:

 def add_detail @order = Order.build @boxes = Company.find(params[:company_id]).boxes @b = @boxes.first @ci = Time.now.to_i respond_with(@order, @boxes, @b, @ci) end 

_details部分

 <%= form_for @order do |f| %> <%= f.fields_for :order_details, child_index: @ci do |d| %> <td><%= d.collection_select :box_id, @boxes, :id, :uid, {}, {class: 'form-control'} %></td> <td><%= d.text_field :quantity, class: 'form-control' %></td> <td><%= d.text_field :box_price, class: 'form-control' %></td> <td><%= d.text_field :cb_price, class: 'form-control' %></td> <td><%= d.text_field :mould_fees, class: 'form-control' %></td> <td>$$$</td> <% end %> <% end %> 

有可能的

这里有一个非常非常好的教程: http : //pikender.in/2013/04/20/child-forms-using-fields_for-through-ajax-rails-way/

我们最近也在我们的一个开发应用程序上实现了这种types的表单。 如果您转到& http://emailsystem.herokuapp.com ,注册(免费)并点击“新消息”。 “订户”部分使用这种技术

顺便说一句,我们手动。 茧实际上看起来非常好,似乎使用与我们相同的原则。 还有一个RailsCast ,但这只适用于单个添加(我认为)


f.fields_for

你这样做的方式是使用一系列dynamic构build你需要的字段的部分。 从你的代码看起来你已经有了基础(表单正在工作),所以现在是构build几个组件来处理AJAX请求的一个例子:

  1. 您需要处理控制器上的AJAX(路由+控制器操作)
  2. 你需要把你的f.fields_for分成部分(所以他们可以用Ajax调用)
  3. 您需要处理模型中的buildfunction

使用控制器处理AJAX

首先,您需要处理控制器中的Ajax请求

要做到这一点,你需要添加一个新的“端点”的路线。 这是我们的:

  resources :messages, :except => [:index, :destroy] do collection do get :add_subscriber end end 

控制器的行为然后转化为:

 #app/controllers/messages_controller.rb #Ajax Add Subscriber def add_subscriber @message = Message.build render "add_subscriber", :layout => false end 

将你的f.fields_for添加到部分

要处理这个问题,你需要把你的f.fields_for分成几部分。 这里是我们的forms的代码forms:

 #app/views/resources/_message_subscriber_fields.html.erb <%= f.fields_for :message_subscribers, :child_index => child_index do |subscriber| %> <%= subscriber.collection_select(:subscriber_id, Subscriber.where(:user_id => current_user.id), :id, :name_with_email, include_blank: 'Subscribers') %> <% end %> #app/views/messages/add_subscriber.html.erb <%= form_for @message, :url => messages_path, :authenticity_token => false do |f| %> <%= render :partial => "resources/message_subscriber_fields", locals: {f: f, child_index: Time.now.to_i} %> <% end %> #app/views/messages/new.html.erb <% child_index = Time.now.to_i %> <div id="subscribers"> <div class="title">Subscribers</div> <%= render :partial => "message_subscriber_fields", locals: {f: f, child_index: child_index } %> </div> 

将您的构buildfunction扩展到您的模型

为了保持干爽,我们在模型中创build了一个build函数,我们可以每次调用它:

  #Build def self.build message = self.new message.message_subscribers.build message end 

CHILD_INDEX

你最好的朋友是child_index

如果你添加了多个字段,你将遇到的大问题是增加字段的[id] (这是我们在Ryan Bates教程中find的缺陷)

我发布的第一个教程解决了这个问题,只是用child_index设置新字段的Time.now.to_i 。 这设置了一个唯一的ID,因为新字段的实际ID是不相关的,你可以添加任意数量的字段,只要你喜欢它


JQuery的

  #Add Subscriber $ -> $(document).on "click", "#add_subscriber", (e) -> e.preventDefault(); #Ajax $.ajax url: '/messages/add_subscriber' success: (data) -> el_to_add = $(data).html() $('#subscribers').append(el_to_add) error: (data) -> alert "Sorry, There Was An Error!"