28

I have an index page that builds a table, and I am trying to allow users to edit line's in the table. I am trying to do this in the most basic way possible - no javascript, ajax, etc, unless Rails is supplying it.

I have my table displaying fine in the index method, and there is a form as the last row in the table that can be used to add a new row. The new form works fine. Each row has an edit link that routes to the controller's edit method. The controller sets the object to be edited, and renders index, this time with a form in the row that is to be edited. My problem is that this form will not submit, but if I refresh the page it will submit.

The fact that the page will submit after a refresh is very confusing. I don't see how a refresh would do anything different then clicking the link (it should still go through the same routing, with the same variables right?) and I can't see any difference in the form html before and after the refresh. Any have ideas on what might be happening?

I am not sure what code to even start looking in, but here goes;

index.html.erb

... <tbody> <% @boms.each do |line| %> <% if line == @bom %> <%= render("form_in_table", form_objects: @bom , button_text: "Update") %> <% else %> <%= render("bom_in_table", line: line) %> <% end %> <% end %> <% if @bom.new_record? %> <%= render("form_in_table", form_objects: [@li, @bom] , button_text: "Add") %> <% end %> </tbody> ... 

_form_in_table.html.erb

<%= form_for(form_objects, html: {class: "form-in-table"}) do |f| %> <tr> <td><%= f.text_field :quantity %></td> <td colspan="2"> <%= f.select(:part_id, options_from_collection_for_select(@parts, :id, :pricebook_name), prompt: "Select a Part",) %></td> <td></td> <td></td> <td></td> <td><%= f.submit(button_text, class: "btn btn-primary btn-mini") %></td> </tr> <% end %> 

_bom_in_table.html.erb

<tr> <td><%= line.quantity%></td> <td><%= line.part_number %></td> <td><%= line.part_description %></td> <td><%= number_to_currency(line.part_cost) %></td> <td><%= line.part_unit %></td> <td><%= number_to_currency(line.extension) %></td> <td><%= link_to('Edit', edit_bom_path(line)) %></td> </tr> 

boms_controller.rb

... def edit @bom = Bom.find(params[:id]) @li = @bom.line_item @boms = @li.boms.sorted_by_part_number @parts = Part.sorted_by_number render 'index' end ... 

In case this is useful to deciphering the code/intent, I have collections of line_items, parts, and boms; line_item has many boms, and line_item has many parts through boms. In addition to the part/line item relationship, boms have a quantity. Bom is short for bill of materials. @li is the line_item that is being manipulated. The form I having trouble with is for viewing/adding/editing the collection of boms (quantitys and parts) that belong to a line item.

ADDING LOGS

Started GET "/line_items/8/boms" for 127.0.0.1 at 2013-10-14 14:27:27 -0400 Processing by BomsController#index as HTML Parameters: {"line_item_id"=>"8"} [1m[35mLineItem Load (0.0ms)[0m SELECT "line_items".* FROM "line_items" WHERE "line_items"."id" = ? LIMIT 1 [["id", "8"]] [1m[36mLineItemSubClass Load (1.0ms)[0m [1mSELECT "line_item_sub_classes".* FROM "line_item_sub_classes" WHERE "line_item_sub_classes"."id" = ? ORDER BY "line_item_sub_classes"."id" ASC LIMIT 1[0m [["id", 8]] [1m[35mLineItemClass Load (4.0ms)[0m SELECT "line_item_classes".* FROM "line_item_classes" WHERE "line_item_classes"."id" = ? ORDER BY "line_item_classes"."id" ASC LIMIT 1 [["id", 1]] Rendered shared/_error_messages.html.erb (3.0ms) [1m[36mBom Load (1.0ms)[0m [1mSELECT "boms".* FROM "boms" INNER JOIN "parts" ON "parts"."id" = "boms"."part_id" WHERE "boms"."line_item_id" = ? ORDER BY "parts".number ASC[0m [["line_item_id", 8]] [1m[35mPart Load (0.0ms)[0m SELECT "parts".* FROM "parts" WHERE "parts"."id" = ? ORDER BY "parts"."id" ASC LIMIT 1 [["id", 1]] Rendered boms/_bom_in_table.html.erb (96.0ms) [1m[36mPart Load (1.0ms)[0m [1mSELECT "parts".* FROM "parts" ORDER BY "parts".number ASC[0m Rendered boms/_form_in_table.html.erb (103.0ms) [1m[35m (24.0ms)[0m SELECT SUM(quantity * cost) AS sum_id FROM "parts" INNER JOIN "boms" ON "boms"."part_id" = "parts"."id" WHERE "boms"."line_item_id" = 8 Rendered boms/index.html.erb within layouts/boms (477.0ms) Rendered layouts/_shim.html.erb (1.0ms) Rendered layouts/_header.html.erb (0.0ms) Rendered layouts/_footer.html.erb (0.0ms) Rendered layouts/application.html.erb (69.0ms) Completed 200 OK in 671ms (Views: 601.0ms | ActiveRecord: 31.0ms) Started GET "/boms/22/edit" for 127.0.0.1 at 2013-10-14 14:28:13 -0400 Processing by BomsController#edit as HTML Parameters: {"id"=>"22"} [1m[36mBom Load (0.0ms)[0m [1mSELECT "boms".* FROM "boms" WHERE "boms"."id" = ? LIMIT 1[0m [["id", "22"]] [1m[35mLineItem Load (1.0ms)[0m SELECT "line_items".* FROM "line_items" WHERE "line_items"."id" = ? ORDER BY "line_items"."id" ASC LIMIT 1 [["id", 8]] [1m[36mLineItemSubClass Load (1.0ms)[0m [1mSELECT "line_item_sub_classes".* FROM "line_item_sub_classes" WHERE "line_item_sub_classes"."id" = ? ORDER BY "line_item_sub_classes"."id" ASC LIMIT 1[0m [["id", 8]] [1m[35mLineItemClass Load (0.0ms)[0m SELECT "line_item_classes".* FROM "line_item_classes" WHERE "line_item_classes"."id" = ? ORDER BY "line_item_classes"."id" ASC LIMIT 1 [["id", 1]] Rendered shared/_error_messages.html.erb (0.0ms) [1m[36mBom Load (1.0ms)[0m [1mSELECT "boms".* FROM "boms" INNER JOIN "parts" ON "parts"."id" = "boms"."part_id" WHERE "boms"."line_item_id" = ? ORDER BY "parts".number ASC[0m [["line_item_id", 8]] [1m[35mPart Load (0.0ms)[0m SELECT "parts".* FROM "parts" ORDER BY "parts".number ASC Rendered boms/_form_in_table.html.erb (25.0ms) [1m[36m (0.0ms)[0m [1mSELECT SUM(quantity * cost) AS sum_id FROM "parts" INNER JOIN "boms" ON "boms"."part_id" = "parts"."id" WHERE "boms"."line_item_id" = 8[0m Rendered boms/index.html.erb within layouts/boms (41.0ms) Rendered layouts/_shim.html.erb (0.0ms) Rendered layouts/_header.html.erb (1.0ms) Rendered layouts/_footer.html.erb (0.0ms) Rendered layouts/application.html.erb (54.0ms) Completed 200 OK in 113ms (Views: 104.0ms | ActiveRecord: 3.0ms) Started GET "/boms/22/edit" for 127.0.0.1 at 2013-10-14 14:28:37 -0400 Processing by BomsController#edit as HTML Parameters: {"id"=>"22"} [1m[35mBom Load (0.0ms)[0m SELECT "boms".* FROM "boms" WHERE "boms"."id" = ? LIMIT 1 [["id", "22"]] [1m[36mLineItem Load (0.0ms)[0m [1mSELECT "line_items".* FROM "line_items" WHERE "line_items"."id" = ? ORDER BY "line_items"."id" ASC LIMIT 1[0m [["id", 8]] [1m[35mLineItemSubClass Load (0.0ms)[0m SELECT "line_item_sub_classes".* FROM "line_item_sub_classes" WHERE "line_item_sub_classes"."id" = ? ORDER BY "line_item_sub_classes"."id" ASC LIMIT 1 [["id", 8]] [1m[36mLineItemClass Load (1.0ms)[0m [1mSELECT "line_item_classes".* FROM "line_item_classes" WHERE "line_item_classes"."id" = ? ORDER BY "line_item_classes"."id" ASC LIMIT 1[0m [["id", 1]] Rendered shared/_error_messages.html.erb (0.0ms) [1m[35mBom Load (1.0ms)[0m SELECT "boms".* FROM "boms" INNER JOIN "parts" ON "parts"."id" = "boms"."part_id" WHERE "boms"."line_item_id" = ? ORDER BY "parts".number ASC [["line_item_id", 8]] [1m[36mPart Load (0.0ms)[0m [1mSELECT "parts".* FROM "parts" ORDER BY "parts".number ASC[0m Rendered boms/_form_in_table.html.erb (5.0ms) [1m[35m (0.0ms)[0m SELECT SUM(quantity * cost) AS sum_id FROM "parts" INNER JOIN "boms" ON "boms"."part_id" = "parts"."id" WHERE "boms"."line_item_id" = 8 Rendered boms/index.html.erb within layouts/boms (27.0ms) Rendered layouts/_shim.html.erb (1.0ms) Rendered layouts/_header.html.erb (8.0ms) Rendered layouts/_footer.html.erb (0.0ms) Rendered layouts/application.html.erb (60.0ms) Completed 200 OK in 131ms (Views: 94.0ms | ActiveRecord: 2.0ms) Started GET "/assets/application.css?body=1" for 127.0.0.1 at 2013-10-14 14:28:38 -0400 Started GET "/assets/custom.css?body=1" for 127.0.0.1 at 2013-10-14 14:28:38 -0400 Started GET "/assets/jquery.js?body=1" for 127.0.0.1 at 2013-10-14 14:28:38 -0400 Started GET "/assets/jquery_ujs.js?body=1" for 127.0.0.1 at 2013-10-14 14:28:38 -0400 Started GET "/assets/bootstrap-transition.js?body=1" for 127.0.0.1 at 2013-10-14 14:28:38 -0400 Started GET "/assets/bootstrap-affix.js?body=1" for 127.0.0.1 at 2013-10-14 14:28:38 -0400 Started GET "/assets/bootstrap-alert.js?body=1" for 127.0.0.1 at 2013-10-14 14:28:38 -0400 Started GET "/assets/bootstrap-collapse.js?body=1" for 127.0.0.1 at 2013-10-14 14:28:38 -0400 Started GET "/assets/bootstrap-modal.js?body=1" for 127.0.0.1 at 2013-10-14 14:28:38 -0400 Started GET "/assets/bootstrap-carousel.js?body=1" for 127.0.0.1 at 2013-10-14 14:28:39 -0400 Started GET "/assets/bootstrap-button.js?body=1" for 127.0.0.1 at 2013-10-14 14:28:39 -0400 Started GET "/assets/bootstrap-dropdown.js?body=1" for 127.0.0.1 at 2013-10-14 14:28:39 -0400 Started GET "/assets/bootstrap-scrollspy.js?body=1" for 127.0.0.1 at 2013-10-14 14:28:39 -0400 Started GET "/assets/bootstrap-tab.js?body=1" for 127.0.0.1 at 2013-10-14 14:28:39 -0400 Started GET "/assets/bootstrap-typeahead.js?body=1" for 127.0.0.1 at 2013-10-14 14:28:39 -0400 Started GET "/assets/bootstrap-tooltip.js?body=1" for 127.0.0.1 at 2013-10-14 14:28:39 -0400 Started GET "/assets/bootstrap-popover.js?body=1" for 127.0.0.1 at 2013-10-14 14:28:39 -0400 Started GET "/assets/bootstrap.js?body=1" for 127.0.0.1 at 2013-10-14 14:28:39 -0400 Started GET "/assets/turbolinks.js?body=1" for 127.0.0.1 at 2013-10-14 14:28:39 -0400 Started GET "/assets/parts.js?body=1" for 127.0.0.1 at 2013-10-14 14:28:39 -0400 Started GET "/assets/application.js?body=1" for 127.0.0.1 at 2013-10-14 14:28:39 -0400 
4
  • 1
    i don't see anything that would make rails behave like you said. could you please add some logs? Commented Oct 14, 2013 at 18:13
  • Ditto - would be great to see some logs. You can get them from /log/development.log file :) Commented Oct 14, 2013 at 18:15
  • Logs are added to the question. Note that I hit the submit button several times before refreshing, but it creates no log entries. I do notice that the refresh GETs several assets that the original loading does not. Is this the problem? Commented Oct 14, 2013 at 18:36
  • The extra GETs are a red herring. I got the form to work (see below) and the working version doesn't GET these assets either. Commented Oct 15, 2013 at 12:07

8 Answers 8

51

I believe this is an HTML issue, not a Rails issue. Per this discussion Form inside a table, <form> can not be placed inside <table> or <tbody> or <tr>. After moving the <form> to wrap the table and putting the controls inside the respective <td> the form works.

I still don't understand why refreshing the page made the form work, but...

Sign up to request clarification or add additional context in comments.

6 Comments

can you please explain what is putting the controls inside <td>
First thought it was a turbolink issue, but later revealed I was using form tag inside a table.
I worked on this for two hours before I found your answer. Tried the turbolink fix, added javascript to push the submit button,etc.
Make sure that the html tags are closed properly and follows html guidelines
It is a combo of incorrect markup and Turbolinks - for me was nesting a form inside a <table>. Reloading worked, which was definitely due to Turbolinks not playing nice with the bad markup
|
17

If it's Rails 4, it's probably because of Turbolinks. Try putting

data-no-turbolink="true" inside your body tag 

This may work, it happend once to me.

1 Comment

This is not a solution, this is a patch, you're disabling Turbolinks :/ Although it's a valid decision, it's not the solution to the problem. Garret and Jerome answers are better.
12

This type of error is most frequently one generated by invalid HTML. Various sources of errors can be:

  • missing < or >
  • HTML tag not closed
  • orphaned HTML closing tag (where no opening one is related); in complex forms I've had extra </div>s lying about...
  • Forms nested within table or tr tags (within td is allowed)

The form helpers need to be properly nested, otherwise these quirks will bite you...

1 Comment

Great...this worked for me. I had a stray </div> that I captured by incrementally deleting inputs in my form until it worked, to isolate the issue.
5

For rails 5, try using data: { turbolinks: false } inside any links to the page containing the form.

E.g. <%= link_to "Get in Touch", 'contact', data: { turbolinks: false } %>

2 Comments

Best answer for people who have no choice but to have a form within a table. This is the only one that worked for me that allows turbolinks to work elsewhere (without if statements for turbolinks of course)
I have a form displayed on a show page, and using data: { turbolinks: false } from the link on the index page allows me to submit the form without refreshing the page. Thanks.
4

Try putting data-no-turbolink="true" into the link that called the table page.

 <a href="/vender" data-no-turbolink="true"> 

That works form me.

Comments

3

I have to share my experience : I played with Turbolinks, like you. But suddently, I had an issue : the previous pages needed to have turbolinks disabled too, to work. After many & many & many hours on the issue, I found the solution : <% f.submit %> was separated by 2 <div>from the rest of the form ! Here is an example: Wrong:

<div class="container"> <div class="row"> <!-- Inscription --> <div class="col-lg-8 contact_col"> <div class="get_in_touch"> <div class="section_title">Modifier une marque</div> <div class="contact_form_container"> <%= form_for @brand, url: {action: "update"} do |f| %> <div class="row"> <div class="col-xl-12"> <!-- Name --> <label for="contact_name">Nom de la marque</label> <%= f.text_field :brand, class: "contact_input" %> </div> <div class="col-xl-12 last_name_col"> <span> <%= f.label "Image de la marque" %><br /> </span> <%= f.file_field :brand_picture %> </div> </div> </div> </div> <button class="newsletter_button trans_200"> <%= f.submit "Modifier" %> </button> <% end %> </div> </div> </div> 

Correct:

 <div class="container"> <div class="row"> <!-- Inscription --> <div class="col-lg-8 contact_col"> <div class="get_in_touch"> <div class="section_title">Modifier une marque</div> <div class="contact_form_container"> <%= form_for @brand, url: {action: "update"} do |f| %> <div class="row"> <div class="col-xl-12"> <!-- Name --> <label for="contact_name">Nom de la marque</label> <%= f.text_field :brand, class: "contact_input" %> </div> <div class="col-xl-12 last_name_col"> <span> <%= f.label "Image de la marque" %><br /> </span> <%= f.file_field :brand_picture %> </div> </div> <button class="newsletter_button trans_200"> <%= f.submit "Modifier" %> </button> <% end %> </div> </div> </div> </div> </div> 

Comments

0

Think your workaround might be to just reload on form submit. So add remote: true to your form. This will just do a quick refresh for you. Please note that this is not a solution but a workaround. A recommended solution would be Garrett Berneche's answer.

<%= form_for(form_objects, html: {class: "form-in-table"}, remote: true) do |f| %> 

Comments

0

for me keeping the form_tag outside the "table" and "tr" worked

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.