1
\$\begingroup\$

I have the following scenario:

 class Product < ActiveRecord::Base
 belongs_to :categories
 end
 class Category < ActiveRecord::Base
 has_many :products
 end

Categories table has 2 level nesting, for example. Main category is 'Men', sub-category is 'Accessories' and sub-sub-category is 'Watches'. enter image description here

Now when user creates new product he must choose category(only main category is required, but he can also chose sub and sub-sub category optional).

My idea is this: I created 3 different select boxes with same name "product[category_id]", so only the last selected value will be sent to the server through params[:product][:category_id].

<div class="col-md-2 main-category">
 <small> Main category? required </small>
 <%= f.select :category_id, 
 Category.where('category_id IS ?', nil).collect {|c| [c.name, c.id]},
 { include_blank: "Select category..." }, 
 id: 'main-category', class: 'form-control' %>
</div>
<div class="col-md-2 category-level-1">
 <small> What type? optional </small>
 <%= f.select :category_id, {}, {}, class: 'form-control' %>
</div>
<div class="col-md-2 category-level-2">
 <small> What type? optional </small>
 <%= f.select :category_id, {}, {}, class: 'form-control' %>
</div>

2nd(sub-categories) and 3rd(sub-sub-categories) are initial empty ({}, {}) and hidden through CSS(display: none) and will be populated through Ajax call.

$('#main-category, .category-level-1 > select').change(function() {
 var selectBox = this.id;
 var selectedValue = $(this).val();
 var url = '/categories/' + selectedValue + '/subcategories';
 $.get(url, function(data) { createSelect(data, selectBox); });
});
function createSelect(data, selectBox) {
 var $currentSelect = null;
 if (selectBox == 'main-category') {
 $('.category-level-1').show();
 $('.category-level-2').hide();
 $('.category-level-1 > select').find('option').remove();
 $currentSelect = $('.category-level-1 > select');
 }
 else {
 $('.category-level-2').show();
 $('.category-level-2 > select').find('option').remove();
 $currentSelect = $('.category-level-2 > select');
 }
 $currentSelect.append('<option selected disabled>Select Category...</option>');
 for(var i=0; i<data.length; i++) {
 $currentSelect.append('<option value="' + data[i].id + '">' + data[i].name +
 '</option>');
 }
}

Where Ajax call is sent to following route (categories controller)

def subcategories
 id = params[:id]
 @subcategories = Category.where('category_id = ?', id)
 render json: @subcategories 
end

So for final result i have following: enter image description here

First of all, is it normal to have different inputs in one form with same names created manually like I did in this example? For some reason, it seem like 'code-smell' to me. I'm relatively new to Rails, so I wanted to ask if I am violating some good practices with this code. Can you suggest better ways of achieving the same results?

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Jan 5, 2014 at 3:32
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

These are nested selects, you can try awesome_nested_set, and use it like this:

<%= f.select :parent_id, 
 nested_set_options(Category, @category) {|i| "#{'-' * i.level} #{i.name}" } %>
answered Feb 25, 2014 at 21:13
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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.