Rails - how to send a form that is inside another form?

16,429

Solution 1

Simply, you can't have two separate nested forms.

You can have nested associated forms using accepts_nested_attributes_for - this is dependent on the structure of your models in the backend, which I'll detail below.

--

Forms

The most important thing you need to know is that Rails just builds HTML forms - which means they have to abide by all the constraints & rules these forms stipulate:

enter image description here

Each form has its own action attribute - which is how your browser knows where to send the data. If you have multiple / nested forms, you'll basically have two of these "action" attributes; preventing HTML from sending the form correctly

--

Nested

If you want to use some Rails conventions, you'll be better using the accepts_nested_attributes_for method. This resides in the model, and in order to use it effectively, you need to ensure you have your ActiveRecord associations set up correctly:

#app/models/user.rb
Class User < ActiveRecord::Base
   has_many :hobbies
   accepts_nested_attributes_for :hobbies
end

#app/models/hobby.rb
Class Hobbies < ActiveRecord::Base
   belongs_to :user
end

The importance of the association is crucial - Rails will pass "nested" model data through to each other. The nested model data is only there if the models are associated correctly with ActiveRecord (as above)

You can then pass the data through your controller / models as follows:

#app/controllers/users_controller.rb
Class UsersController < ApplicationController
   def new
       @user = User.new
       @user.hobbies.build # -> essential for nested attributes 
   end

   def create
      @user = User.new(user_params)
      @user.save #-> notice how this saves the @user model only?
   end

   private

   def user_params
      params.require(:user).permit(:user, :attributes, hobbies_attributes: [:hobbies, :attributes])
   end
end

This will allow you to include the hobbies part of the form as follows:

<%= form_for @user do |f| %>
   <%= f.fields_for :hobbies do |h| %>
       <%= h.text_field :your_attributes %>
   <% end %>
<% end %>

Solution 2

Nested forms is not valid html. To achieve this you have to either un-nest your forms or use javascript. If you opt for the javascript approach, you simple catch the form submit event, prevent the default, and manually submit the form you want.

EDIT:

If you opt for the javascript approach, consider that you might not need two forms at all. Instead you can just add a click handler for the button on your 'sub action' which makes an ajax request.

Alternatively, if javascript is not an option, and rearranging the html is not an option, you could handle this on the server by submitting the data from both forms and processing each differently depending on which submit input was clicked.

Solution 3

You can't have a form inside a form. If you have your associations correctly setup somthing like

class User < ActiveRecord::Base
  has_many :hobbies

  accepts_nested_attributes_for :hobbies
end

class Hobby < ActiveRecord::Base
  belongs_to :user
end

then you can use fields_for in your form like

<%= form_for @user do |u| %>
  // user fields
  <%= u.fields_for :hobbies do |h| %>
    // hobbies fields
  <% end %>
  <%= f.submit %>
<% end %>

This will give you params like

users_attributes: { name: "xyz", hobbies_attributes: { attribute: "value"} }  

For details checkout accept_nested_attributes_for

Share:
16,429
user984621
Author by

user984621

Updated on July 23, 2022

Comments

  • user984621
    user984621 almost 2 years

    I have this structure in my view:

    <%= simple_form_for @user ... %>
      ...
      ...
      <%= render '/hobbies/form', :user => @user %>
      ...
      ...
    <% end %>
    

    The render part loads data from a partial - there's a form. The form-fields are successfully loaded (like inputs, submit input, radio buttons etc), but when I take a look at the generated HTML, in the rendered form is missing <%= form_for... and the closing tag.

    Which means when I click to the submit button from the rendered form, this rendered form is not sent out, but instead of it is sent out the "big" for - simple_form_for.

    How to make the rendered form sendable?

    Thank you

    • BroiSatse
      BroiSatse almost 10 years
      You cannot have two nested forms, You'll need to move the inner one out.
    • Joel Brewer
      Joel Brewer over 9 years
      As noted by the answer by @thedeeno below, you could use JavaScript to achieve functionality similar to nested forms.
  • BenKoshy
    BenKoshy almost 8 years
    extremely useful thank you sir - do you know or have any examples of how to: "catch the form submit event, prevent the default, and manually submit the form you want."? that would be very helpful