Show only subcategories of the selected category

This is the code I have:

<%= f.collection_select :category_id, Category.all, :id, :name, {prompt: "Choose a category"} %>
<%= f.collection_select :subcategory_id, Subcategory.all, :id, :name, {prompt: "Choose a subcategory"} %>

      

All it does is display all categories in one category dropdown and all subcategories in others . egthe drop downs.

Question: How to create only specific subcategories depending on the selected main category. With the above code all categories and all subcategories are displayed.

Everything is related to the model, has_many and belongs_to ... and category_id and subcategory_id .. everything is working correctly, I just don't know how to show specific subcategories in the selected category.

My attempt:

  <% if (Category.where(:name=>"Intro")) do |d| %>
    <% d.subcategories.each do |subcategory| %>
      <%= link_to subcategory.name, gigs_path(subcategory: subcategory.name) %>
    <% end %>
  <% end %>

      

This code gives an error. I would say if, for example, a user selects a category called "Intro" than a list of all "subcategories". But that didn't work - my code is clearly wrong.

Thank.

+3


source to share


4 answers


I assume you want this to happen without reloading the page? Then you won't get a little JavaScript. Here's a general workflow:

  • when the category dropdown has changed, submit an Ajax request that will respond to JavaScript and paste the correct values ​​into the subcategory dropdown.

For this example, I'm just assuming your form (a model belonging to a category) is a post. You didn't seem to mention the model.

Let's try this:

views / posts / _form :
I'll wrap the selct field of the subcategories in a div subcategories_select

, which will allow us to replace all the content later.

<%= f.collection_select :category_id, Category.all, :id, :name, { prompt: "Choose a category" }, id: "category_id" %>

<div id="subcategories_select">
 <%= f.collection_select :subcategory_id, Subcategory.all, :id, :name, { prompt: "Choose a subcategory" }, { disabled: true } %>
</div>

      

assets /JavaScripts/posts.js.erb

# select the element to watch for changes
$('form').on('change', '#category_id'), function() {
  var category_id = $(this).val(); # save the category_id set in the first dropdown

  $.ajax({
    url: "/categories/" + category_id + "/get_subcategories", # a custom route, see routes.rb further down
    type: "GET",
    dataType: "script", # we expect a response in js format
    data: { "category_id": category_id } # the only value we will need to get the subcategories
  });
});

      



config / routes.rb

# we need a custom route for our get_subcategories action
resources :categories do
  member do
    get :get_subcategories, defaults: { format: "js" }
  end
end

      

Controllers / posts_controller.rb
Important: I am assuming the has_many subcategories and subcategories have category_id, which means the next category. If it's not next, it doesn't make sense.

# our custom controller action
def get_subcategories
  @subcategories = Subcategory.where(category_id: params[:category_id])
end

      

app / views / posts / get_subcategories.js.erb
Here we replace the content of our subcategories_select div and insert a collection_select with the appropriate parameters.

$('#subcategories_select').html("<%= j collection_select(:post, :category_id, @subcategories, :id, :title), { prompt: 'Select subcategory...' }, { disabled: false } %>");

      

Note that I did this from the beginning, so there might be bugs, but this is one approach to dynamically filling select boxes or changing anything on the page at all without reloading.

+7


source


This is what solved my question

in javascript folder (gem-gem must be installed)

$ ->
  $('#gigs').imagesLoaded ->
    $('#gigs').masonry
      itemSelector: '.box'
      isFitWidth: true

  $(document).on 'change', '#gig_category_id', (evt) ->
    $.ajax 'update_sub_categories',
      type: 'GET'
      dataType: 'script'
      data: {
        category_id: $("#gig_category_id option:selected").val()
      }
      error: (jqXHR, textStatus, errorThrown) ->
        console.log("AJAX Error: #{textStatus}")
      success: (data, textStatus, jqXHR) ->
        console.log("Dynamic country select OK!")

      

in controller Gig

respond_to :html, :js, :json
  def update_sub_categories
    @cats = Subcategory.where(category_id: params[:category_id]).all
    respond_with(@cats)
  end

      

Than I have created a partial view as gig _subcategory.html.erb and put this code



<option value="<%= cat.id %>"><%= cat.name %></option>

      

than another partial in gig view called _update.html.erb put this code

$("#gig_subcategory_id").empty().append("<%= escape_javascript(render(:partial => "subcategory", :collection => @cats, :as => :cat)) %>")

      

Finally, in the view, to display the category and subcategory, I used

  <%= f.collection_select :category_id, Category.all, :id, :name, {prompt: "Choose a category"} %>
  <%= f.collection_select :subcategory_id, Subcategory.all, :id, :name, {prompt: "Choose a subcategory"} %>

      

+1


source


Filtering collection_select with js can be very tricky. I recommend using group_collection_select, which filters the data if the relationship is correct. If you have a data model that looks something like the above answer, the view should have something like this:

views / posts / _form:

<%= f.collection_select(:category_id, Category.all, :id, :name,    
               { prompt: 'Select a category' }, { id: 'category-select' }) %>

<%= f.grouped_collection_select :subcategory_id, Category.all, :subcategories, 
          :name, :id, :name, { include_blank: 'Select a sub category' },
                                               { id: 'subcategory-select' } %>

      

At the moment, you can see both select forms, but the grouped_collection_select field shows nested options. in order to show only the required subcategories, we will need to make some changes in javascript, in my case I am using coffee, and my files will be named. coffee instead of .js.erb

App / assets / javascripts / posts.coffee:

jQuery ->
  subcat = $('#subcategory-select').html()
  $('#category-select').change ->
    cat = jQuery('#category-select').children('option').filter(':selected').text()
    options = $(subcat).filter("optgroup[label='#{cat}']").html()
    if options
      $('#subcategory-select').html(options)
    else
      $('#subcategory-select').empty()

      

I owe my answer to this where you can find more details on using group_collection_select

+1


source


This way, the embedded ruby ​​that you wrote is evaluated before being sent to the "client side". This means that the code you wrote is turned into static HTML and sent to the user.

You want to use something that can provide logical operations in the user's browser. This means javascript.

Checkout: AngularJS: https://angularjs.org/

0


source







All Articles