6

I'm using the cocoon gem on my rails app and I have two nested fields inside a form (categories, subcategories). Initially I'm showing the first one only and the second one is hidden. Each time the first select fields has subcategories the second field gets populated and appears.

The nested fields:

<div class="nested-fields">

  <%= form.input :category, collection: @categories, as: :grouped_select, group_method: :children, :label => false, :include_blank => true, input_html: { id: "first_dropdown" } %>

<div class="show_hide">
  <%= form.input :subcategories, label: false, :collection => [], :label_method => :name, :value_method => :id, required: true, input_html: { multiple: true, id: "second_dropdown" } %>
</div>

  <div class="links">
    <%= link_to_remove_association "Remove", form %>
  </div>
</div>

This is the code to initially hide the second field

$('#first_dropdown').keyup(function() {

    if ($(this).val().length == 0) {
        $('.show_hide').hide();
    }
}).keyup();

This is the code to show the second select input when the first select input has subcategories:

def select_item
  category = Category.find params[:category_id]
  render json: category.children
end


$('#first_dropdown').on('change', function() {
    $.ajax({
        url: 'categories/select_item?category_id=' + $(this).val(),
        dataType: 'json',
        success: function(data) {
            let childElement = $('#second_dropdown');
            if( data.length === 0 ) {
                $('.show_hide').hide();
            } else {
                $('.show_hide').show();
            }
            childElement.html('');
            data.forEach(function(v) {
                let id = v.id;
                let name = v.name;
                childElement.append('<option value="' + id + '">' + name + '</option>');
            });
        }
    });
});

Everything works well for the initial opened field. But when I add more fields and select a value inside any of the first select fields it populates all of the second fields according to that value. I think it's because I'm using specific id's for this and when I add more fields, all of them end up having the same id's. How can I set this up so I properly populate the second select field each time I add more nested fields to the form?

Dev
  • 437
  • 6
  • 25

1 Answers1

5

I'd give your selects classes instead of ids:

<div class="nested-fields">
  <%= form.input :category, collection: @categories, as: :grouped_select, group_method: :children,
                 label: false, include_blank: true, input_html: { class: "first_dropdown" } %>

  <% if f.object.category && f.object.category.sub_categories.length > 0 %>
    <div class="show_hide">
      <%= form.input :subcategories, label: false, collection: form.object.category.subcategories, label_method: :name,
                     value_method: :id, required: true, input_html: { multiple: true, class: "second_dropdown" } %>
    </div>
  <% else %>
    <div class="show_hide" style="display: none;">
      <%= form.input :subcategories, label: false, collection: [], label_method: :name,
                     value_method: :id, required: true, input_html: { multiple: true, class: "second_dropdown" } %>
    </div>
  <% end %>
  <div class="links">
    <%= link_to_remove_association "Remove", form %>
  </div>
</div>

then find the second select relative to the first one adding this to a page specific js file:

$(document).on('turbolinks:load cocoon:after-insert', function(){
  $('.first_dropdown').off('change').on('change', function(){
    let $this = $(this);
    let $second = $this.closest('.nested-fields').find('.second_dropdown');
    $second.children().remove();
    $second.closest('.show_hide').hide();
    if ($this.val() !== '') {
      $.ajax({
        url: 'categories/select_item?category_id=' + $(this).val(),
        dataType: 'json',
        success: function(data){
          if (data.length > 0) {
            $second.append('<option value=""></option>');
            data.forEach(function(v) {
              let id = v.id;
              let name = v.name;
              $second.append('<option value="' + id + '">' + name + '</option>');
            });
            $second.closest('.show_hide').show();
          }
        }
      })
    }
  });
});
Ben Trewern
  • 1,583
  • 1
  • 10
  • 13
  • Thanks for the reply @Ben Trewern.. unfortunately it's not populating the second select input correctly. For example if I choose a category in the first select input the second select input shows and gets populated correctly. After that if I add one more field and select a category in the first select input, the second select input gets populated correctly but the previous second select input changes and gets populated according to the category I just choose. Any ideas how I can fix this? – Dev Mar 07 '20 at 18:20
  • I've changed my answer above which may fix your issue. – Ben Trewern Mar 07 '20 at 20:28
  • Thanks for your time and help @Ben Trewern. The second select input gets populated correctly now but I'm noticing one last issue. The second select input show/hide feature is not working correctly. For example: When I add more fields to the form the second select input is not showing initially(which is correct) but when I choose a category that has subcategories, the second select input shows but on all fields, if no subcategories exist, then it hides on all fields. How can we make the second select input show/hide feature independent on each pair of fields that I add? – Dev Mar 08 '20 at 09:42
  • Where is the show_hide class in your erb? I'm guessing you have a div around your subcategories select with class show_hide. – Ben Trewern Mar 08 '20 at 15:33
  • Check out my question, I just added the code that initially hides the div `.show_hide` and then If you check the `ajax` code I'm doing show/hide feature again. – Dev Mar 08 '20 at 15:37
  • How are you initializing your js? Are you using Turbolinks? – Ben Trewern Mar 08 '20 at 15:48
  • Nope, I'm not using Turbolinks anymore. I have the javascript inside a tag under the rails code inside a html.erb view. If that's the info your are asking about. – Dev Mar 08 '20 at 15:53
  • Updated my answer. Does that fix it? – Ben Trewern Mar 08 '20 at 20:28
  • The only issue now is... that the field that is already open doesn't populate or show/hide the second select field. But if I add more fields everything works correctly on all fields. – Dev Mar 09 '20 at 09:11
  • Unfortunately I'm getting the same behavior as I last mentioned :( – Dev Mar 09 '20 at 15:04
  • I've had one more go at it. – Ben Trewern Mar 14 '20 at 17:25
  • Sorry for the delayed reply @Ben Trewern, I just saw your message. I think we are almost there. The only issue now is that the second field appears even if the subcategories are empty. And it's not suppose to appear when the subcategories are empty. This happens on the fileds I add and the exiting one. – Dev Mar 18 '20 at 07:48
  • Updated my answer. – Ben Trewern Mar 18 '20 at 22:54
  • Back to the same issue... the field that is already open doesn't populate or show/hide the second select field. But if I add more fields everything works correctly on all fields. Btw I'm not using turbolinks. – Dev Mar 19 '20 at 14:21