Rails accepts_nested_attributes_for with f.fields_for and AJAX

后端 未结 1 785
猫巷女王i
猫巷女王i 2020-11-28 12:55

I\'m curious how to properly use accepts_nested_attributes_for and f.fields_for.

views/orders/new.html.erb



        
相关标签:
1条回答
  • 2020-11-28 13:30

    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!"
    
    0 讨论(0)
提交回复
热议问题