2

I have this loop in my view:

<% @item.inventory_items.each do |p| %>

I have this form that is looped in the above do block for each inventory_item:

<%= form_for(@list_item) do |f| %>
             <%= f.hidden_field :upc, :value => @item.upc %>
             <%= f.hidden_field :inventory_item_id, :value => p.id %> 

             <%= f.select :shopping_list_id, options_for_select(ShoppingList.options_for_list(current_user.shopping_lists)), {}, :class => "form-control" %>
             <%= f.submit("Add It!", class: "btn btn-add") %>
<% end %>

I've now put this form into a Bootstrap 3 modal. For brevity's sake I won't post the whole modal. Here's the trigger, which is looped for each inventory_item:

<button class="btn btn-primary btn-lg" data-toggle="modal" data-target="#formModal">Add To List</button>

The problem I'm running into, is that my form uses attributes from the looped inventory_items to populate one of the hidden fields in this form. So here's my question:

How can I pass this variable from the loop to a modal when it is triggered?

EDIT

Here's my current modal:

<div class="modal fade" id="addListItem" tableindex="-1" role="dialog" aria-labelledby="addListItem" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
          <h4 class="modal-title" id="form-label">Add this item to a shopping list.</h4>
      </div>

      <div class="modal-body">
         <%= form_for(@list_item) do |f| %>
             <%= f.hidden_field :upc, :value => @item.upc %>
             <%= f.hidden_field :inventory_item_id, :id => "inventory-item-id" %> 

             <%= f.select :shopping_list_id, options_for_select(ShoppingList.options_for_list(current_user.shopping_lists)), {}, :class => "form-control" %>
             <%= f.submit("Add It!", class: "btn btn-add") %>
         <% end %>
      </div>

      <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
      </div>

    </div>     
  </div> 
</div>

Custom .js from @David Antaramian:

$('button,[data-inventory-item-id]').click(function() {
    var inventoryID = $(this).data('inventory-item-id');
    $('#inventoryID').prop('value', inventoryID);
    $('#addListItem').modal('show');
});
settheline
  • 3,333
  • 8
  • 33
  • 65
  • I don't understand the looping in your code. What I assume you are trying to do is use the "Add To List" button you have created to activate the modal containing the form for an individual `inventory_item` at which point the user can select which `ShoppingList` to save that item to. What I am trying to grasp is: is the `form_for` loop a child statement of the `@item-do-p` loop? Or is it separate? Is there a separate modal for each form or is it all defined in one large modal? – David Antaramian Feb 18 '14 at 03:36
  • Sorry, I should clarify. `form_for` is a child statement of the `@item-do-p` loop. I only have one modal, which I had hoped could be populated with the correct hidden field attributes from the loop, depending which `inventory_item` button was selected. Your assumption about my final goals is correct. Does that clear it up? – settheline Feb 18 '14 at 03:50
  • Edited my question to clarify. – settheline Feb 18 '14 at 03:52
  • Yes, that clears things up. I'm trying to think of ways that this could be accomplished using only a single modal dialog. – David Antaramian Feb 18 '14 at 03:53
  • 1
    I think these SO threads are similar, and should help you: http://stackoverflow.com/a/20724640/1422070 and http://stackoverflow.com/a/10635652/1422070 – edwardmp Feb 20 '14 at 02:10
  • Thanks edward. I was able to use the second SO thread to get me there after some modifications for rails. – settheline Feb 27 '14 at 20:06

2 Answers2

0

As it stands, my opinion is that you should not be doing a form_for inside of your @item-do-p loop. The modal needs to be populated with the form to save a single item to a ShoppingList, but in order to do that it must have an inventory_item_id. The form itself has the same data properties except for that input element.

To implement this, I am going to use jQuery and custom data attributes. In the view file, I have the following code:

<div class="modal fade" id="addListItem">
    <div class="modal-dialog">
        <div class="model-content">
            <div class="modal-header">
                <button class="close" data-dismiss="modal">&times;</button>
            </div>
            <div class="modal-body">
                    <%= form_for(@list_item) do |f| %>
                        <%= f.hidden_field :upc, :value => @item.upc %>
                        <%= f.hidden_field :inventory_item_id, :id => "inventory-item-id" %> 
                        <%= f.select :shopping_list_id, options_for_select(ShoppingList.options_for_list(current_user.shopping_lists)), {}, :class => "form-control" %>
                        <%= f.submit("Add It!", class: "btn btn-add") %>
                    <% end %>
            </div>
        </div>
    </div>
</div>

<div class="container">
    <% @item.inventory_items.each do |p| %>
        <div class="row">
            <div class="col-md-8">
                <%= p.name %>
            </div>
            <div class="col-md-4">
                <button class="btn btn-primary btn-lg" data-inventory-id="<%= p.id %>">Add To List</button>
            </div>
        </div>
    <% end %>
</div>

In the controller-specific JavaScript asset file, add the following code:

$(document).ready(function() {
    $('button,[data-inventory-id]').click(function() {
        var inventoryID = $(this).data('inventory-id');
        $('#inventoryID').prop('value', inventoryID);
        $('#addListItem').modal('show');
    });
});

There is one form located in the modal. In the container, we have repeating rows for each inventory item in @item. The "Add to List" button now has a custom data- tag, data-inventory-id, which is set from the @item-do-p loop. This tag specifies which inventory item we are adding to the ShoppingList.

The JavaScript code first selects all elements that are both button elements and have a data-inventory-id property. It registers a click() listener on all of them. When one of them is clicked, it gets the inventory ID of the item, assigns it to the inventory ID hidden field in the form, and then shows the form.

Please let me know if I misunderstood what you were trying to do.

David Antaramian
  • 4,145
  • 1
  • 23
  • 16
  • This is exactly what I'm trying to do. I made a few changes to your answer (changing "inventory-id" to "inventory-item-id"). For some reason, the modal is not responding to button clicks. I'm not very competent with js, so I might have the js file in the wrong place. I have it in `views/items/show.js`. Do I need to include it somewhere? Thank you! – settheline Feb 18 '14 at 21:24
  • That was my mistake for calling it the "view-specific" file. It should be in the controller-specific JavaScript asset file which, in your case, would most likely be `app/assets/javascripts/items.js.coffee`. I wrote this in JavaScript, so you should change the extension to just `.js`. The asset pipeline handles concatenating it into the main `application.js` file which should be included in your layout by default. You can make includes controller specific, but this requires configuration. See further [RailsGuides: The Asset Pipeline](http://guides.rubyonrails.org/asset_pipeline.html) – David Antaramian Feb 18 '14 at 22:01
  • I see. I went ahead and did as suggested. I'm still not getting any response from button clicks. As I said, I'm not very competent with js, so I'm at a bit of a loss when it comes to troubleshooting this. Any ideas? – settheline Feb 18 '14 at 23:33
  • Is your console generating an error? Using Chrome, you can bring the console up by clicking on View -> Developer -> JavaScript Console. Using Firefox: Tools -> Developer -> Web Console. The console should be logging any JavaScript errors. If no errors occur when you click on the button, then the .click() event callback is not specified correctly. The anonymous function that acts as the callback is dependent on the modal (that is, the `div` with class `modal`) having an ID attribute of `addListItem` and the target hidden field having an ID attribute of `inventoryID`. – David Antaramian Feb 19 '14 at 00:27
  • Didn't get any errors in the console. I've updated my question with the full modal and my js file. I couldn't find any glaring errors, do you see anything? – settheline Feb 19 '14 at 00:54
  • Just a few thoughts, since I don't know what customizations you have done to your app configuration. **1.)** Do you still have `gem 'jquery-rails'` in your `Gemfile`? The code above depends on you having jQuery installed for the selectors. **2.)** Do you still have `//= require jquery` in your `assets/javascripts/application.js`? **3.)** Do you have a line like `<%= javascript_include_tag "application", "data-turbolinks-track" => true %>` or similar somewhere in your main layout file (default is `application.html.erb`) or whichever layout the view in question inherits from? – David Antaramian Feb 19 '14 at 20:44
  • Yes on all except for `"data-turbolinks-track" => true`. I just added that and am still not getting any response from the buttons... – settheline Feb 20 '14 at 01:26
  • Sorry, I think the major problem was that I forgot to wrap the statement in a jQuery listener for the DOM-ready event. The altered JavaScript source should work now. If not, I think the question is beyond my ability to help you. – David Antaramian Feb 20 '14 at 02:05
  • Hmmmm...still not getting any response from the buttons after adding that listener. I appreciate your input though. – settheline Feb 26 '14 at 06:12
0

After being pointed to this thread by edward above, I was able to tweak a few things to work well with rails: Passing data to a bootstrap modal

Button Trigger:

<%= content_tag "button", class: "btn btn-default btn-lg addbtn", data: {id: "#{p.id}", toggle: "modal", target: "#addListItem"} do %>
          Add To List
<% end %>

Modal:

<div class="modal fade" id="addListItem" tableindex="-1" role="dialog" aria-labelledby="addListItem" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
          <h4 class="modal-title" id="form-label">Add this item to a shopping list.</h4>
      </div>

      <div class="modal-body">
         <%= form_for(@list_item) do |f| %>
             <%= f.hidden_field :upc, :value => @item.upc %>
             <%= f.hidden_field :inventory_item_id, :id => "inventoryID", :value => "" %> 

             <%= f.select :shopping_list_id, options_for_select(ShoppingList.options_for_list(current_user.shopping_lists)), {}, :class => "form-control" %>
             <%= f.submit("Add It!", class: "btn btn-add") %>
         <% end %>
      </div>

      <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
      </div>

    </div>     
  </div> 
</div>

JS:

$(document).on("click", ".addbtn", function() {
        var inventoryID = $(this).data('id');
        $("#inventoryID").val(inventoryID);
});
Community
  • 1
  • 1
settheline
  • 3,333
  • 8
  • 33
  • 65