1

I have a Rails 4 form that builds a list of parts using AJAX on the form page. Once the list of parts is built in the <ul> I want to submit the list as an array of values in a parameter in the params hash.

My form:

<%= form_for ([@tool, @service]),:html => { :onSubmit => 'getParts' } do |f| %>
  <% if @service.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@service.errors.count, "error") %> prohibited this service from being saved:</h2>

      <ul>
      <% @service.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

   <div class="row">

    <div class="span4 offset1">

  <div class="form-inline">
    <%= f.hidden_field :tool_id , :value=>params[:tool_id] %>
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </div>
 <br>
  <br>
  <div class="form-inline">
    <%= f.label :due_date %><br>
    <%= f.text_field :due_date, 'data-behaviour' => 'datepicker' %>
  </div>
 <br>
  <br>
  <div class="form-inline">
    <%= f.label :completed %><br>
    <%= f.text_field :completed, 'data-behaviour' => 'datepicker' %>
  </div>
 <br>
   <div class="field">
     <%= f.label 'Service Type:' %>
      <%= f.select :service_type_id, ServiceType.all.collect{|s| [s.name, s.id] }, {include_blank: false} %>
   </div>
   <br>

  </div>

 <div class="span7">
   <div class="form-inline">
     <div class="part_list_element">
     Parts Used <br>
     <%= text_field_tag :parts_used %>
     </div>
   </div>
   <br>

     Default Parts:
     <ul id="serv_parts_list">

     </ul>
   <br>
   <br>

    <div class="form-inline">
      <%= f.label :note %><br>
      <%= f.text_area :note %>
    </div>
   <br>
   <br>
    <div class="actions">
    <%= f.submit  %>
    </div>

 </div>
</div>

<% end %>

When a user chooses a Default Service from the selection list, this javascript fires:

$('#service_service_type_id').change(function() {
    var id = this.value;
    $.ajax
    ({
        url:'/get_default_parts',
        type:"POST",
        data: {
          service_type: {
            id:  id
          }
        }
    });
  });

This gets a list of parts from the DB based on the selection. It then returns an array via JS that then gets appended to the empty Default Parts list:

  Default Parts:
     <ul id="serv_parts_list">

     </ul>

becomes:

 <ul id="serv_parts_list">
     <li id="042511060272">042511060272 - Stihl/Denso Spark Plug W22mp-u<a class="service_parts">&lt;-Remove</a></li>
     <li id="795711145835">795711145835 - Bar Oil - Stihl<a class="service_parts">&lt;-Remove</a></li>
     <li id="795711478179">795711478179 - Stihl MS660/066 Air Filter<a class="service_parts">&lt;-Remove</a></li>
 </ul>

In my application.js I have:

$(document).ready(function(){
  $("#commit").click(function() {
    $("#expended_parts").val(('#serv_parts_list').text());
  });
});

But this doesn't do anything. I've been trying to figure out how to get the <li> elements in the <ul> into an array and passed as a param. But current params hash looks like:

Parameters: {"utf8"=>"✓",   "authenticity_token"=>"s/....=", "service"=> 
 {"tool_id"=>"113", "name"=>"test without hash", "due_date"=>"", "completed"=>"", 
  "service_type_id"=>"2", "note"=>""}, "parts_used"=>"", "commit"=>"Create Service"}  

Since the list of parts can get updated multiple times after the page loads, either from an AJAX call to get the default parts list that gets appended to the <li>, I think this would have to be bound to the submit event. Any help is appreciated.

2
  • Would you mind sharing your solution to this issue? I'm going through the same right now. Commented Dec 24, 2014 at 8:38
  • @seong-Lee see my answer below. Commented Dec 31, 2014 at 3:07

2 Answers 2

2

I was overthinking this and found a much easier answer. It has been a long time since I've worked on this so I've had to go back over my work to refresh my memory.

Basically I had the Javascript that builds inserts the default parts and any parts added as just a list of checkboxes with their boxes already checked. That way if the user decides they don't need one of the default parts, or want to remove a part they can just uncheck the box and it doesn't get submitted with the form. The empty checkbox lists start out in form shown above like this:

     Default Parts:<br>
        <div id="default_parts_list" class="checkbox inline">

        </div>
          <br>
     Other Parts:<br>
        <div id="parts_expended" class="checkbox inline">

        </div>
   <br>
   <br>

when the dropdown for the type of service is changed on the form it fires the javascript shown in my question above. This gets the parts list and hands it off to this JS:

$("#default_parts_list").empty();

$("#default_parts_list").append('<%= escape_javascript(render :partial => 'get_default_parts' ) %>');

which renders the _get_default_parts partial thus:

  <%= collection_check_boxes(:service, :part_ids, @parts_list, :id, :sku_name, {}, {checked: true}) do |b| %>
  <%= b.label { b.check_box + b.object.name + " " + b.object.sku} %>
  <% end %>

this creates html appended to the form like this:

<div id="default_parts_list" class="checkbox inline">

    <label for="service_part_ids_14">
       <input checked="checked" id="service_part_ids_14" name="service[part_ids][]" type="checkbox" value="14">Stihl/Denso Spark Plug W22mp-u 042511060272
    </label>

    <label for="service_part_ids_24">
        <input checked="checked" id="service_part_ids_24" name="service[part_ids][]" type="checkbox" value="24">Bar Oil - Stihl 795711145835
    </label>

    <label for="service_part_ids_10">
        <input checked="checked" id="service_part_ids_10" name="service[part_ids][]" type="checkbox" value="10">Stihl MS660/066 Air Filter 795711478179
    </label>

    <input name="service[part_ids][]" type="hidden" value="">
</div>

So they are just plain old checkboxes that end up being submitted with the form and end up in the params hash like this:

Parameters: {"utf8"=>"✓", "authenticity_token"=>"IHXcV0H8NnySxGIBXi8ZA=", 
  "service"=>{"tool_id"=>"121", "name"=>"refresher", "due_date"=>"2014-12-27", 
  "completed"=>"2014-12-27", "service_type_id"=>"2", 
  "part_ids"=>["14", "24", "10", ""], "note"=>""}, "parts_used"=>"", 
  "commit"=>"Create Service", "tool_id"=>"121"}

When this same form is used to edit and exiting service it has to use a different form rather than the form partial above. The edit form looks like this:

<h1>Editing service for <%=" #{@tool.category.name.singularize} / #{@tool.serial}" %></h1>

<%= form_for ([@tool, @service]) do |f| %>
<% if @service.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@service.errors.count, "error") %> prohibited this service from being saved:</h2>

      <ul>
        <% @service.errors.full_messages.each do |msg| %>
            <li><%= msg %></li>
        <% end %>
      </ul>
    </div>
<% end %>

<div class="row">

  <div class="span4 offset1">

    <div class="form-inline">
      <%= f.hidden_field :tool_id , :value=>params[:tool_id] %>
      <%= f.label :name %><br>
      <%= f.text_field :name %>
    </div>
    <br>
    <br>
    <div class="form-inline">
      <%= f.label :due_date %><br>
      <%= f.text_field :due_date, 'data-behaviour' => 'datepicker' %>
    </div>
    <br>
    <br>
    <div class="form-inline">
      <%= f.label :completed %><br>
      <%= f.text_field :completed, 'data-behaviour' => 'datepicker' %>
    </div>
    <br>
    <div class="field">
      <%= f.label 'Service Type:' %>
      <%= f.select :service_type_id, ServiceType.all.collect{|s| [s.name, s.id] }, {include_blank: false} %>
    </div>
    <br>

  </div>

  <div class="span7">
    <div class="form-inline">
      <div class="part_list_element">
        Parts Used <br>
        <%= text_field_tag :parts_used %>
      </div>
    </div>
    <br>
    <strong class="alert-danger">Any parts that get unchecked here will be returned to inventory</strong><br>
    <b>Parts Used:</b> <br>
    <div id="parts_used_list" class="checkbox inline">

          <%= f.collection_check_boxes :part_ids, @service.parts, :id, :name %>


    </div>
    <br>
    <b>Other Parts:</b> <br>
    <div id="parts_expended" class="checkbox inline">

    </div>
    <br>
    <br>

    <div class="form-inline">
      <%= f.label :note %><br>
      <%= f.text_area :note %>
    </div>
    <br>
    <br>
    <div class="actions">
      <%= f.submit  %>
    </div>

  </div>
</div>

<% end %>

I hope this helps anyone doing something similar. You can view the full source app on github: EquipDB on github.com

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

1 Comment

@seong-lee Let me know if you have any more questions.
0

You have to remember Rails doesn't care about HTMl, so when you mention you want to send "<ul> list as params", it's got nothing to do with the <ul>, but how you're porting those items into Rails


Routes

The most robust way to do this is to use a route, and pass the params through JS to it:

#config/routes.rb
post "your_route(/:items)", to: "controller#action"

#app/views/parts/index.html.erb
 <ul id="serv_parts_list">
     <li id="1">1</li>
     <li id="2">2</li>
     <li id="3">3</li> 
 </ul>
 <%= link_to "Submit", class: "submit_link" %>

#app/assets/application.js.erb
$("a.submit_link").on("click", function() { 
    var data = jQuery('#serv_parts_list li').map(function(){
         return 'id[]=' + this.id.match(/(\d+)$/)[1]
    }).get()

    $.ajax ({
       url: "your_route",
       data: data.join('&')
       success: function(data) {
           //success
       },
       error: function(data) {
           //error
       }
    });
});

2 Comments

Will this submit all of my form elements (the others on the page listed above) in one params hash? I was also thinking I could use hidden form values for each list item. I'm not sure I understand your explanation. I get that Rails doesn't care about html, but the form structure is what submits the nested params hash so I am just trying to fit into that.
I understand that the <ul> has nothing to do with the params. My question was COULD I use JS to set an array variable to the values in the array upon submit AND also return all of my other form params. Rather than pass all of my params through JS I set the JS update to build a collection of checkboxes. That way the user can leave them all selected for the default parts list, or uncheck the ones they don't want to use. Thank you though for the effort.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.