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

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

问题描述

我很好奇,如何正确地使用 accepts_nested_attributes_for f.fields_for 。这是我的工作迄今... HTTP://www.ecorebox。 COM /命令/新?的company_id = 1

的意见/命令/ new.html.erb

 <%=的form_for @order,HTML:{角色:形式}办| F | %>

  <%= f.submit不要推...,遥控器:真%>
  <%= f.text_field:发票%>
  &其中;%= f.text_field:ordered_on%GT;
  &其中;%= f.text_field:delivered_on%GT;

  <表ID ='order_form'>
    < H3>详情< / H3>
    < TBODY>
      <%=渲染'order_details /详细信息,F:F%>
    < / TBODY>
    <%=的link_to'添加行',new_order_detail_path(的company_id:PARAMS [:的company_id]),遥控器:真%>
    <%=的link_to'新的盒子,new_box_path,遥控:真%>
  < /表>

<%结束%GT;
 

的意见/ order_details / _details.html.erb

 < TR类='ROW0'>
  <%= f.fields_for:order_details,CHILD_INDEX:CHILD_INDEX办| D | %>
    < TD><%= d.collection_select:box_id,@boxes,:ID,:UID,{},
                                {名字:box_id类:表格控}%GT;< / TD>
    < TD><%= d.text_field:数量,等级:表格控%>< / TD>
    < TD><%= d.text_field:box_price,等级:表格控%>< / TD>
    < TD><%= d.text_field:cb_price,等级:表格控%>< / TD>
    < TD><%= d.text_field:mould_fees,等级:表格控%>< / TD>
    < TD> $$$< / TD>
  <%结束%GT;
< / TR>
&所述; TR类='box0'>
  < TD合并单元格=6>&放大器;#8594; < B><%= @ b.uid%>< / B> |长度:LT;%= @ b.length个%> |宽度:<%= @ b.width%> |高度:LT;%= @ b.height%> |重量:<%= @ b.weight%>< / TD>
< / TR>
 

控制器/ orders_controller.rb (我是pretty的肯定,这是不对的......这里的任何帮助将不胜AP preciated)

 高清创建
  @order = Order.create(PARAMS [:为了])
  如果@ order.save
    闪光[:成功=订单#{@order.invoice}加!
    redirect_to时CURRENT_USER
  其他
    渲染的订单/新
  结束
结束
 

型号/ order.rb

 类订单<的ActiveRecord :: Base的
  attr_accessible ...:order_details_attributes
  的has_many:order_details
  accepts_nested_attributes_for:order_details
结束
 

我已经能够得到部分发挥不错的唯一方法是如果我真的叫 fields_for fields_for Order.new。 order_details.build 。但是,这并不建立在所有的嵌套的对象。我需要使用 f.fields_for 命名建立秩序,和的OrderDetail。我只能建的,虽然。这是我的下一个问题。

请参阅如何有按钮在那里?它AJAXs行到表单中。如果点击添加行,我得到

  NameError在Order_details#新
显示D:/Dropbox/Apps/rails_projects/erbv2/app/views/order_details/new.js.erb其中,3行提出:
未定义的局部变量或方法'F'的#<#<类别:0x5cf0a18>:0x5cbd718>
 

的意见/命令/ add_detail.js.erb

  $('#order_form tr.total')之前(<%= j的渲染部分:订单/详细信息,当地人:{F:@f,CHILD_INDEX: @ci}%>中)
 

我不知道如何定义 F ...我检查了<一href="http://stackoverflow.com/questions/371147/rails-ajax-my-partial-needs-a-formbuilder-instance">Rails AJAX:我的部分需要FormBuilder实例和其他几个人

这是我应该如何处理这有什么建议?使用code我这里......我能创造一个新的秩序,与相关order_details,但box_id没有保存,和的company_id没有保存。我知道这是种讨厌的,但我不知道还有什么地方去了。

更新

路线:

 资源:订单做
  集合{获得:add_detail}
结束

这是比具有用于细节的单独的资源更好。我没想到这之前!
 

HTML表单:

 &LT;%=的form_for @order,的company_id:PARAMS [:的company_id],HTML:{角色:形式}办| F | %&GT;
  F。 ...
  &LT;%=渲染部分:详细信息,当地人:{F:F}%GT; #first孩子
  &LT;%=的link_to'添加行',add_detail_orders_path(的company_id:PARAMS [:的company_id]),遥控器:真%&GT; #add随后的孩子
&LT;%结束%GT;
 

订单控制器:

 高清add_detail
  @order = Order.build
  @boxes = Company.find(PARAMS [:的company_id])。拖曳
  @b = @ boxes.first
  @ci = Time.now.to_i
  respond_with(@order,@boxes,@b,@ci)
结束
 

_details部分

 &LT;%=的form_for @order办| F | %&GT;
  &LT;%= f.fields_for:order_details,CHILD_INDEX:@ci办| D | %&GT;
    &LT; TD&GT;&LT;%= d.collection_select:box_id,@boxes,:ID,:UID,{},
                                {类​​:表格控}%GT;&LT; / TD&GT;
    &LT; TD&GT;&LT;%= d.text_field:数量,等级:表格控%&GT;&LT; / TD&GT;
    &LT; TD&GT;&LT;%= d.text_field:box_price,等级:表格控%&GT;&LT; / TD&GT;
    &LT; TD&GT;&LT;%= d.text_field:cb_price,等级:表格控%&GT;&LT; / TD&GT;
    &LT; TD&GT;&LT;%= d.text_field:mould_fees,等级:表格控%&GT;&LT; / TD&GT;
    &LT; TD&GT; $$$&LT; / TD&GT;
  &LT;%结束%GT;
&LT;%结束%GT;
 

解决方案

可以

有一个关于这个在这里一个非常,非常好的教程:<一href="http://pikender.in/2013/04/20/child-forms-using-fields_for-through-ajax-rails-way/">http://pikender.in/2013/04/20/child-forms-using-fields_for-through-ajax-rails-way/

我们最近也实现了这个类型的形式对我们开发的应用程序之一。如果您转到&功放; http://emailsystem.herokuapp.com ,注册(免费),然后单击新邮件。在订阅部分使用这种技术

顺便说一句,我们这样做手工。茧实际上看起来真的很不错,而且似乎使用相同的原则我们。还有一个 RailsCast ,但是这仅适用于单一的补充(我认为)


f.fields_for

您做到这一点的方法是使用一系列的谐音而动态地构建你所需要的字段。从code,它看起来像你在的地方基本面(形式是工作),所以现在它的建设几个组件来处理Ajax请求的情况下:

  1. 您需要处理AJAX的控制器(路由+控制器动作)
  2. 您需要把你的f.fields_for成谐音(这样他们就可以被称为与阿贾克斯)
  3. 您需要处理模型构建功能

处理AJAX与控制器

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

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

 资源:消息:除了=&GT; [:索引:摧毁]做
    集合做
       得到:add_subscriber
    结束
 结束
 

控制器动作,然后翻译成:

 #应用程序/控制器/ messages_controller.rb
#Ajax添加用户
高清add_subscriber
    @message = Message.build
    渲染add_subscriber:布局=&GT;假
结束
 


添加您的 f.fields_for 进入谐音

要解决这个问题,你需要把你的 f.fields_for 成谐音。这里是code组成表单:

 #应用程序/视图/资源/ _message_subscriber_fields.html.erb
&其中;%= f.fields_for:message_subscribers,:CHILD_INDEX =&GT; CHILD_INDEX做|用户| %&GT;
    &LT;%= subscriber.collection_select(:subscriber_id,Subscriber.where(:USER_ID =&GT; current_user.id):身份证,:name_with_email,include_blank:认购人)%GT;
&LT;%结束%GT;

#应用程序/视图/邮件/ add_subscriber.html.erb
&LT;%=的form_for @message,:URL =&GT; messages_path,:authenticity_token =&GT;假做| F | %&GT;
        &LT;%=渲染:部分=&GT; 资源/ message_subscriber_fields,当地人:{F:女,CHILD_INDEX:Time.now.to_i}%GT;
&LT;%结束%GT;

#应用程序/视图/邮件/ new.html.erb
&LT;%CHILD_INDEX = Time.now.to_i%&GT;
&LT; D​​IV ID =用户&GT;
    &LT; D​​IV CLASS =头衔 -  GT;认购&LT; / DIV&GT;
    &LT;%=渲染:部分=&GT; message_subscriber_fields,当地人:{F:女,CHILD_INDEX:CHILD_INDEX}%GT;
&LT; / DIV&GT;
 


扩展你的构建功能,您的型号

要保持干燥的东西,我们就创建了一个建立在模型中,我们可以调用每个时间功能:

  #Build
    高清self.build
       消息= self.new
       message.message_subscribers.build
       信息
    结束
 


<一个href="http://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for#512-Setting-child-index-while-using-nested-attributes-mass-assignment">Child_Index

您在这里最好的朋友是 CHILD_INDEX

如果您要添加多个字段,最大的问题你必须是递增的字段 [ID] (这是我们发现与瑞恩·贝茨缺陷教程)

第一个教程我张贴解决这个问题的方法就是刚才设置的 CHILD_INDEX 的新领域与 Time.now.to_i 。这将设置一个唯一的ID,而且由于新的领域的实际ID是无关紧要的,你就可以作为你想用它添加尽可能多的领域


JQuery的

  #Add用户
    $  - &GT;
      $(文件)。在点击,#add_subscriber,(E) -  GT;
         即preventDefault();

         #Ajax
         $就
           网址:'/消息/ add_subscriber
           成功:(数据) - &GT;
               el_to_add = $(数据)。html的()
               $('#用户)。追加(el_to_add)
           误差:(数据) - &GT;
               警惕对不起,有错误!
 

I'm curious how to properly use accepts_nested_attributes_for and f.fields_for. This is what I'm working for so far... http://www.ecorebox.com/orders/new?company_id=1

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 (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

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.

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} %>")

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

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.

UPDATE

routes:

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:

<%= 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 %>

Orders Controller:

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 %>

解决方案

It Is Possible

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/

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

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)


f.fields_for

The way you do it is to use a series of partials which dynamically build the fields you need. From your code, it looks like you have the fundamentals in place (the form is working), so now it's a case of building several components to handle the AJAX request:

  1. You need to handle the AJAX on the controller (route + controller action)
  2. You need to put your f.fields_for into partials (so they can be called with Ajax)
  3. You need to handle the build functionality in the model


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:

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


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:

#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>


Extend Your Build Functionality To Your Model

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

Your best friend here is child_index

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)

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天全站免登陆