Rails accepts_nested_attributes_for con f.fields_for y AJAX

Tengo curiosidad por usar adecuadamente accepts_nested_attributes_for y f.fields_for .

views / orders / new.html.erb

      

Details

views / order_details / _details.html.erb

        $$$    →  | length:  | width:  | height:  | weight:   

controladores / orders_controller.rb (estoy bastante seguro de que esto está mal … cualquier ayuda aquí sería muy apreciada)

 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 

modelos / order.rb

 class Order < ActiveRecord::Base attr_accessible ..., :order_details_attributes has_many :order_details accepts_nested_attributes_for :order_details end 

La única forma en que he podido obtener el parcial para jugar bien es si realmente llamo a los fields_for como fields_for Order.new.order_details.build . Pero eso no construye el objeto nested en absoluto. Necesito usar la nomenclatura f.fields_for para comstackr el Order y OrderDetail. Sin embargo, solo puedo construir uno. Cuál es mi próximo problema.

¿Ves cómo hay botones ahí? AJAX filas en el formulario. Si haces clic en add line , obtengo

 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 #<#:0x5cbd718> 

views / orders / add_detail.js.erb

 $('#order_form tr.total').before("") 

No sé cómo definir f … Revisé Rails AJAX: Mi parcial necesita una instancia de FormBuilder y algunas otras.

¿Alguna sugerencia sobre cómo debo manejar esto? Usando el código que tengo aquí … Pude crear un nuevo orden, con un order_details asociado, pero el box_id no guardó, y el company_id no guardó. Sé que esto es desagradable, pero no sé a dónde más ir.

ACTUALIZAR

rutas:

 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! 

Forma HTML:

  f. ...  #first child  #add subsequent children  

Controlador de pedidos:

 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 

_detalles parciales

        $$$   

Es posible

Hay un muy buen tutorial sobre esto aquí: http://pikender.in/2013/04/20/child-forms-using-fields_for-through-ajax-rails-way/

Recientemente implementamos este tipo de formulario en una de nuestras aplicaciones de desarrollo. Si ingresa a http://emailsystem.herokuapp.com , regístrese (gratis) y haga clic en “Nuevo mensaje”. La parte “Suscriptores” usa esta tecnología

Por cierto lo hicimos manualmente. Cocoon realmente se ve muy bien y parece usar los mismos principios que nosotros. También hay un RailsCast , pero esto solo funciona para adiciones individuales (creo)


f.fields_for

La forma en que lo hace es usar una serie de parciales que construyen dinámicamente los campos que necesita. Desde su código, parece que tiene los fundamentos en su lugar (el formulario está funcionando), por lo que ahora se trata de crear varios componentes para manejar la solicitud AJAX:

  1. Necesita manejar el AJAX en el controlador (ruta + acción del controlador)
  2. Debes poner tus f.fields_for en parciales (para que puedan ser llamados con Ajax)
  3. Debe manejar la funcionalidad de build en el modelo

Manejo de AJAX con el controlador

En primer lugar, debe manejar las solicitudes de Ajax en el controlador

Para hacer esto, debe agregar un nuevo “punto final” a las rutas. Esto es nuestro:

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

La acción del controlador se traduce en:

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

Agregue sus f.fields_for en f.fields_for

Para manejar esto, necesitas poner tus f.fields_for en parciales. Aquí está el código de nuestro formulario:

 #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 %> 
Subscribers
<%= render :partial => "message_subscriber_fields", locals: {f: f, child_index: child_index } %>

Extienda su funcionalidad de construcción a su modelo

Para mantener las cosas secas, acabamos de crear una función de build en el modelo, que podemos llamar cada vez:

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

Child_Index

Tu mejor amigo aquí es child_index

Si está agregando múltiples campos, el gran problema que tendrá es incrementar el [id] del campo (este fue el error que encontramos con el tutorial de Ryan Bates)

La forma en que el primer tutorial que child_index resolvió que esto era simplemente establecer child_index de los nuevos campos con Time.now.to_i . Esto establece una identificación única, y como la identificación real del nuevo campo es irrelevante, podrá agregar tantos campos como desee con ella.


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