我很好奇如何正确使用accepts_nested_attributes_for
和f.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 Order.new.order_details.build
。但这根本不构建嵌套对象。我需要使用f.fields_for
命名法来构建Order和OrderDetail。不过,我只能建一个。这是我的下一期。
看看那里有按钮?它将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: My partial needs a FormBuilder instance和其他几个。
有关我应该如何处理的任何建议?使用我在这里的代码......我能够创建一个新的订单,带有相关的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 partial
<%= 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 %>
答案 0 :(得分:20)
可能
这里有一个非常非常好的教程:http://pikender.in/2013/04/20/child-forms-using-fields_for-through-ajax-rails-way/
我们最近还在其中一个开发应用上实现了此类表单。如果你转到&amp; http://emailsystem.herokuapp.com,注册(免费)并点击“新消息”。 “订户”部分使用此技术
BTW我们手动完成了这项工作。 Cocoon看起来真的很好,似乎使用与我们相同的原则。还有一个RailsCast,但这仅适用于单个添加(我认为)
<强> f.fields_for 强>
你这样做的方法是使用一系列部分动态构建你需要的字段。从您的代码中,看起来您已经掌握了基础知识(表单正在运行),所以现在需要构建几个组件来处理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教程中找到的缺陷)
我发布的第一个教程解决了这个问题,就是用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!"