Rails accepts_nested_attributes_for 和 f.fields_for 和 AJAX [英] Rails accepts_nested_attributes_for with f.fields_for and AJAX

查看:23
本文介绍了Rails accepts_nested_attributes_for 和 f.fields_for 和 AJAX的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm curious how to properly use accepts_nested_attributes_for and 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">&#8594; <b><%= @b.uid %></b> | length: <%= @b.length %> | width: <%= @b.width %> | height: <%= @b.height %> | weight: <%= @b.weight %></td>
</tr>

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

controllers/orders_controller.rb (I'm pretty sure this is wrong... any help here would be greatly appreciated)

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

models/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.不过我只能建一个.这是我的下一个问题.

The only way I've been able to get the partial to play nice is if I actually call the fields_for as fields_for Order.new.order_details.build. But that doesn't build the nested object at all. I need to use the f.fields_for nomenclature to build the Order, and the OrderDetail. I can only build one, though. Which is my next issue.

看到里面的按钮了吗?它将行 AJAX 转换为表单.如果你点击add line,我得到

See how there are buttons in there? It AJAXs rows into the form. If you click add line, I get

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>

views/orders/add_detail.js.erb

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

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

I don't know how to define f... I checked out Rails AJAX: My partial needs a FormBuilder instance and a few others.

关于我应该如何处理这个问题有什么建议吗?使用我在这里的代码...我能够创建一个新订单,并带有关联的 order_details,但是 box_id 没有保存,并且 company_id 没有保存.我知道这有点讨厌,但我不知道还能去哪里.

Any suggestions on how I should handle this? Using the code I have here... I was able to create a new order, with an associated order_details, but the box_id didn't save, and the company_id didn't save. I know this is kind of nasty, but I don't know where else to go.

更新

路线:

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/

There's a very, very good tutorial on this here: http://pikender.in/2013/04/20/child-forms-using-fields_for-through-ajax-rails-way/

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

We also recently implemented this type of form on one of our development apps. If you goto & http://emailsystem.herokuapp.com, sign up (free) and click "New Message". The "Subscribers" part uses this technology

顺便说一句,我们是手动完成的.Cocoon 实际上看起来非常好,并且似乎使用与我们相同的原则.还有一个 RailsCast,但这仅适用于单个添加(我认为)<小时>

f.fields_for

BTW we did this manually. Cocoon actually looks really good, and seems to use the same principles as us. There's also a RailsCast, but this only works for single additions (I think)

你这样做的方法是使用一系列的部分来动态构建你需要的字段.从您的代码来看,您似乎具备了基础知识(表单正在运行),因此现在是构建多个组件来处理 AJAX 请求的案例:

f.fields_for

  1. 您需要在控制器上处理 AJAX(路由 + 控制器操作)
  2. 您需要将 f.fields_for 放入部分中(以便可以使用 Ajax 调用它们)
  3. 您需要处理模型中的build 功能

<小时>

使用控制器处理 AJAX

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


Handling AJAX With The Controller

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

Firstly, you need to handle the Ajax requests in the controller

To do this, you need to add a new "endpoint" to the routes. This is ours:

控制器动作然后转化为:

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

The controller action then translates into:

<小时>

将您的 f.fields_for 添加到部分

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

要解决这个问题,您需要将 f.fields_for 放入部分.这是我们表单的代码:


Add Your f.fields_for Into Partials

To handle this, you need to put your f.fields_for into partials. Here is the code form our form:

<小时>

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

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

To keep things dry, we just created a build function in the model, which we can call each time:

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

<小时>

Child_Index

你最好的朋友是child_index

如果您要添加多个字段,则会遇到的大问题是增加字段的 [id](这是我们在 Ryan Bates 的教程中发现的缺陷)

If you're adding multiple fields, the big problem you'll have is incrementing the [id] of the field (this was the flaw we found with Ryan Bates' tutorial)

我发布的第一个教程解决这个问题的方法是使用 Time.now.to_i 设置新字段的 child_index.这会设置一个唯一的 id,因为新字段的实际 ID 无关紧要,您可以使用它添加任意数量的字段

The way the first tutorial I posted solved this was to just set the child_index of the new fields with Time.now.to_i. This sets a unique id, and because the actual ID of the new field is irrelevant, you'll be able to add as many fields as you like with it

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!"

这篇关于Rails accepts_nested_attributes_for 和 f.fields_for 和 AJAX的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆