Question

I'm using Rails 3.2.13, and then Nested_Form gem to create a shipment header record and multiple shipments detail records. Everything works great, I can add, remove detail line items and save correctly. What I'd like to do now is add some UJS or Ajax to the Material select box that's part of the nested fields_for section of the form. When a material is selected, the "Current Balance" label next to it would be updated with the correct inventory number for each line item.

enter image description here

This is my first foray into UJS and Ajax and I'm a bit lost on how to accomplish this task. Thus far the relevant part of my form looks this:

new.html.erb:

<div class="col-md-12 form-group">
<div class="panel panel-default">
    <table class="table table-striped table-hover" id="details" >
        <th>Material</th>
        <th>Current Balance</th>    
        <th>Net Weight</th>
        <th>Gross Weight</th>
        <th></th>       
        <%= f.fields_for :downstreamDetails, :wrapper => false do |dd| %>


        <tr class="fields">
        <td><%= dd.select :tb_product_type_id, options_from_collection_for_select( @productTypes, :id, :product_type), { :prompt => "Select Material"} , {:class =>"form-control col-sm-2", :data => { remote: true, :url => url_for(:controller => 'downstreams', :action => 'getInventoryData', :paroductType => @productType ,format: 'js') }  }   %></td>       
        <td nowrap="true"><label> <%= number_with_delimiter(@currentBal) %> lbs</label></td>

        <td><%= dd.text_field :ship_total_net_weight, :class =>"form-control col-sm-2" %></td>
        <td><%= dd.text_field :ship_total_gross_weight, :class =>"form-control col-sm-2" %></td>
        <td><%= dd.link_to_remove "Remove", :data => {:target => "#details"}, :class => "btn btn-primary btn-small btn-block" %></td>
        </tr>       

        <% end %>   
    </table>
    </div>

    <div class="row">   
    <div class="col-md-2"><%= f.link_to_add "Add Material" ,:downstreamDetails, :data => {:target => "#details"}, :class => "btn btn-primary btn-small btn-block" %></div>  
    </div>



    </div>
    <div class="row" >
    <div class="col-md-9"></div>    
    <div class="col-md-3"><%= f.submit "Submit", class: "btn btn-small btn-primary" %></div> 
    </div>
<% end %>
</div>

and my controller action:

def getInventoryData

 @tb_product_type = Downstream.new(params[:downstream])
 @product_id = @tb_product_type.downstreamDetails.first.tb_product_type_id
 @product = ProductType.find(@product_id)
 @downstream_ids = Downstream.select("id").where(:to_company_id => current_user.company_id, :is_verified => true).accessible_by(current_ability)
 @currentBal = DownstreamDetail.select("sum(receive_total_net_weight) as currentBal").where(:downstream_id => @downstream_ids, :tb_product_type_id => @product )

 respond_to do |format|
    format.html { redirect_to create_url }
    format.js 
 end  

end

I have a getInventoryData.js.erb but am currently at a loss for what to put in it. Currently it just gives an alert, which works.

The questions I have are:

  • Is there a way to submit just the tb_product_type_id when a material is selected from the select box? Currently, I have to navigate through the params via params[:downstream] (the parent record) to get the id. I'd like to just be able to say @tb_product_type_id = param[:tb_product_type_id] in the controller action

  • I don't think the existing code that uses @currentBal in the controller and the view will work when I add more than one line item... how can I refactor it to handle one or more line items?

  • What do I need in my getInventory.js.erb file to correctly change the label for the "Current Balance" column knowing that I'll have to handle multiple line items? (Remember I'm using Nested_Form Gem)

  • Am I headed in the right direction or is there another way to go about this that's much easier/cleaner?

Thanks in advance!

UPDATE

I've got everything working except the getInventory.js.erb file, still looking for help here. Was able to get the "Current Balance" label to be updated on the first line item but as I originally suspected it didn't work on the multiple line items. So I changed the controller code to grab the totals for all materials like this:

def getInventoryData


 @downstream_ids = Downstream.select("id").where(:to_company_id => current_user.company_id, :is_verified => true).accessible_by(current_ability)
 @currentBals = DownstreamDetail.select("tb_product_type as material, sum(receive_total_net_weight) as currentBal").where(:downstream_id => @downstream_ids).group(:tb_product_type)

 respond_to do |format|
    format.html { redirect_to create_url }
    format.js 
 end  

end

With this controller code I think I just need to be able to iterate through the result set and match it with the selected material, then update the label tag that directly follows in the select tag. Thats where I'm still struggling, How can I use jquery to navigate the DOM and find the correct label to update? I've used this javascript to find all select box values but I can't seem to get the next element and update it:

$('select > option:selected').each(function() {
    alert($(this).text() + ' ' + $(this).val());

    <% @currentBals.each do |c| %>
    if( $(this).text() == '<%= c.material %>' ) {   
    alert(document.getElementById($(this).parent()))
    $(this).parent().next("label").innerHTML = "<%=  number_with_delimiter(c.currentBal) %> lbs"
    alert("<%= c.material %>");
    }
    <% end %>    

});
Was it helpful?

Solution

So taking in mind the update that I made earlier (specifically to the controller code), I've solved this problem using the nested_form gem as originally stated and ajax+UJS. The final piece to the puzzle was the following code in my getInventory.js.erb file:

$( ".fields" ).each( function(){    
    <% @currentBals.each do |c| %>
        if( $(this).find("option:selected").text() == '<%= c.material %>' ) {   
            $(this).find("label").html("<%=  number_with_delimiter(c.currentBal) %> lbs")   
        }
    <% end %>    

});

I had to iterate through the elements under the "fields" class (required by nested_form), find the selected option in each line item, compare it to each item in the currentBals hash populated in the controller action. When the materials matched, I then update the label tag directly adjacent to the select box with the correct balance.

Hope this helps anyone new to ajax/ujs, as I am, or trying add a little extra functionality to what's already built-in to the nested_form gem.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top