我很好奇如何正确使用accepts_nested_attributes_for和f.fields_for。
accepts_nested_attributes_for
f.fields_for
views / orders / 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 %>
views / 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_foras fields_for Order.new.order_details.build。但这根本无法构建嵌套对象。我需要使用f.fields_for命名法来构建Order和OrderDetail。我只能建一个。这是我的下一个问题。
fields_for
fields_for Order.new.order_details.build
看看那里有按钮吗?它将AJAXs行插入表单。如果单击add line,我会得到
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…我签出了RailsAJAX:我的部分需要一个FormBuilder实例和一些其他实例。
f
关于如何处理此问题的任何建议?使用这里的代码…我能够创建一个新的订单,并带有相关的order_details,但是box_id没有保存,而company_id也没有保存。我知道这有点讨厌,但我不知道该去哪里。
更新
路线:
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/
我们最近还在我们的一个开发应用程序中实现了这种形式的表格。如果您转到&http://emailsystem.herokuapp.com,请注册(免费),然后单击“新消息”。“订户”部分使用此技术
顺便说一句,我们手动执行此操作。茧实际上看起来非常好,并且似乎使用与我们相同的原理。还有一个RailsCast,但这仅适用于单个添加项(我认为)
您的方法是使用一系列局部函数,这些局部函数可动态构建所需的字段。从您的代码看来,您已经具备了基础知识(表单正在运行),因此现在是构建几个组件来处理AJAX请求的情况:
build
使用控制器处理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放入局部。这是我们表单的代码形式:
#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>
将构建功能扩展到模型
为了保持干燥,我们仅build在模型中创建了一个函数,每次可以调用该函数:
#Build def self.build message = self.new message.message_subscribers.build message end
儿童索引
你最好的朋友是 child_index
child_index
如果要添加多个字段,将[id]面临的最大问题是增加字段的字段(这是我们在Ryan Bates的教程中发现的缺陷)
[id]
我发布的第一个教程解决此问题的方法是只使用设置child_index新字段Time.now.to_i。这将设置一个唯一的ID,并且由于新字段的实际ID无关紧要,因此您可以根据需要添加任意多个字段
Time.now.to_i
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!"