Rails的accept_nested_attributes_for与f.fields_for和AJAX一起使用

Dud*_*udo 13 ruby ajax jquery ruby-on-rails ruby-on-rails-3

我很好奇如何正确使用accepts_nested_attributes_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 %>
Run Code Online (Sandbox Code Playgroud)

视图/ 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">&#8594; <b><%= @b.uid %></b> | length: <%= @b.length %> | width: <%= @b.width %> | height: <%= @b.height %> | weight: <%= @b.weight %></td>
</tr>
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

车型/ order.rb

class Order < ActiveRecord::Base
  attr_accessible ..., :order_details_attributes
  has_many :order_details
  accepts_nested_attributes_for :order_details
end
Run Code Online (Sandbox Code Playgroud)

我能够让局部发挥出色的唯一方法就是如果我真的称之为fields_foras 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>
Run Code Online (Sandbox Code Playgroud)

意见/命令/ add_detail.js.erb

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

我不知道如何定义f...我检查了Rails AJAX:我的部分需要一个FormBuilder实例和其他几个.

关于我应该如何处理的任何建议?使用我在这里的代码......我能够创建一个新的订单,带有相关的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!
Run Code Online (Sandbox Code Playgroud)

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 %>
Run Code Online (Sandbox Code Playgroud)

订单控制器:

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
Run Code Online (Sandbox Code Playgroud)

_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 %>
Run Code Online (Sandbox Code Playgroud)

Ric*_*eck 20

有可能的

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

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

顺便说一下,我们是手动完成的 Cocoon看起来真的很好,似乎使用与我们相同的原则.还有一个RailsCast,但这仅适用于单个添加(我认为)


f.fields_for

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

  1. 你需要在控制器上处理AJAX(路由+控制器动作)
  2. 你需要把f.fields_for放到partials中(这样可以用Ajax调用它们)
  3. 您需要处理build模型中的功能

使用控制器处理AJAX

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

为此,您需要向路径添加新的"端点".这是我们的:

 resources :messages, :except => [:index, :destroy] do
    collection do
       get :add_subscriber
    end
 end
Run Code Online (Sandbox Code Playgroud)

控制器操作然后转换为:

#app/controllers/messages_controller.rb
#Ajax Add Subscriber
def add_subscriber
    @message = Message.build
    render "add_subscriber", :layout => false
end
Run Code Online (Sandbox Code Playgroud)

添加你的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>
Run Code Online (Sandbox Code Playgroud)

将构建功能扩展到您的模型

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

   #Build
    def self.build
       message = self.new
       message.message_subscribers.build
       message
    end
Run Code Online (Sandbox Code Playgroud)

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!"
Run Code Online (Sandbox Code Playgroud)

  • 感谢您指出这一点 - 我打算写一个教程,因为我没有看到任何其他质量相当的教程.此外,该教程适用于Rails 3.您是否希望我这样做并发布链接? (3认同)