27

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
Garrett Berneche
  • 1,049
  • 1
  • 9
  • 13
  • 1
    i don't see anything that would make rails behave like you said. could you please add some logs? – phoet Oct 14 '13 at 18:13
  • Ditto - would be great to see some logs. You can get them from /log/development.log file :) – Richard Peck Oct 14 '13 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? – Garrett Berneche Oct 14 '13 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. – Garrett Berneche Oct 15 '13 at 12:07

7 Answers7

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

Community
  • 1
  • 1
Garrett Berneche
  • 1,049
  • 1
  • 9
  • 13
  • can you please explain what is putting the controls inside – SNEH PANDYA May 03 '14 at 15:11
  • 2
    First thought it was a turbolink issue, but later revealed I was using form tag inside a table. – Anwar Jul 15 '16 at 14:59
  • 1
    I worked on this for two hours before I found your answer. Tried the turbolink fix, added javascript to push the submit button,etc. – wibberding Nov 17 '16 at 01:27
  • Make sure that the html tags are closed properly and follows html guidelines – Big D May 18 '17 at 13:36
  • It is a combo of incorrect markup and Turbolinks - for me was nesting a form inside a . Reloading worked, which was definitely due to Turbolinks not playing nice with the bad markup
    – Jarvis Johnson Oct 31 '17 at 02:36
  • I worked on this for a lot of time. In my case I was not using a table. I had a form with different `row`s. The `simple_form_for` helper was inside one of the rows while the `submit` button was inside another row. Moving the simple_form_for helper outside all of the rows fixed the issue for me. – pinkfloyd90 Oct 29 '21 at 12:50
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.

carlosveucv
  • 819
  • 8
  • 10
  • Spent three hours looking for this solution. Thank yas. – iamtoc May 16 '14 at 02:54
  • 2
    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. – Uriel Hernández Jun 01 '16 at 21:05
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...

Jerome
  • 5,583
  • 3
  • 33
  • 76
  • 1
    Great...this worked for me. I had a stray that I captured by incrementally deleting inputs in my form until it worked, to isolate the issue. – GhostRider Jun 26 '16 at 11:36
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 } %>

stevec
  • 41,291
  • 27
  • 223
  • 311
  • 1
    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) – uno Oct 10 '19 at 07:21
  • 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. – ejaco Aug 04 '20 at 19:00
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.

jlerma
  • 71
  • 4
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>
OBrooks
  • 343
  • 1
  • 3
  • 7
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| %>
Talita
  • 190
  • 7