I have Rails 5.2.2 and I'm trying to implement the ancestry gem.
So what I want:
I want a user to create an offer for an automotive part, but in the new form I want to be able to choose the category/subcategories and then enter the other details that I have, and then submit the form to create the offer. Let's say that someone wants to add for selling Brake Pads. But first has to select the parent categories. E.g
Car -> Brakes -> Brake Pads
So after choosing Brake Pads, he can create the offer.
What I have:
#category.rb
class Category < ApplicationRecord
has_ancestry
has_many :parts
end
-
#part.rb
class Part < ApplicationRecord
belongs_to :category
end
For now, I have already created one in the console just to make it work: Eg
car = Category.create!(name: "Car")
brakes = Category.create!(name: "Brakes", parent: car)
brake_pads = Category.create!(name: "Brake Pads", parent: brakes)
I already also run the migration rails g migration add_category_to_parts category:references
.
And my view:
#views/parts/new.html.haml
.container
%h2.center Create offer
= simple_form_for(@part, html: {class: "form-group" }) do |f|
.form-group.col-md-8.col-md-offset-2.well
= f.input :title
= f.input :make_name, label: "Make"
= f.input :code, label: 'Part number'
= f.association :condition, label_method: :name, prompt: "-"
= f.input :description
.form-actions
= f.button :submit, class: "btn btn-primary btn-dark-blue"
The question is: How I can render the categories/subcategories in my views-> parts -> new.html.haml form with 3 dropdowns(one for each subcategory because I will have many categories/subcategories) so the user can choose them and then create the offer?
You need CategoriesController in any case, because you need dependent dropdowns here. When user selects category in the first one, you need to make AJAX request to get subcategories and so on
# config/routes.rb
resources :categories, only: [] do
get :select_item, on: :collection
end
# app/assets/javascripts/application.js
$('#first-dropdown').on('change', function() {
$.ajax({
url: 'categories/select_item?category_id=' + $(this).val(),
dataType: 'json',
success: function(data) {
var childElement = $('#second-dropdown');
childElement.html('');
data.forEach(function(v) {
var id = v.id;
var name = v.name;
childElement.append('<option value="' + id + '">' + name + '</option>');
});
}
});
});
# app/controllers/categories_controller.rb
Class CategoriesController < ApplicationController
def select_item
category = @category.find params[:category_id]
render json: category.children
end
end
# app/inputs/fake_select_input.rb
class FakeSelectInput < SimpleForm::Inputs::CollectionSelectInput
def input(wrapper_options = nil)
label_method, value_method = detect_collection_methods
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options).merge(input_options.slice(:multiple, :include_blank, :disabled, :prompt))
template.select_tag(
attribute_name,
template.options_from_collection_for_select(collection, value_method, label_method, selected: input_options[:selected], disabled: input_options[:disabled]),
merged_input_options
)
end
end
# views/parts/new.html.haml
.form-group.col-md-8.col-md-offset-2.well
= f.input :title
= f.input :make_name, label: "Make"
= f.input :code, label: 'Part number'
= f.association :condition, label_method: :name, prompt: "-"
= f.input :description
= f.input :parent_category_id, as: :fake_select, collection: Category.roots, input_html: { id: 'first-dropdown' }
= f.input category_id, collection: [], input_html: { id: 'second-dropdown' }
.form-actions
= f.button :submit, class: "btn btn-primary btn-dark-blue"
Since simple_form relies on using a model you need to create custom input for non-model fields (parent categories). You need to save only one category_id to @part, parent categories can always be taken from it. If you need more, than 2 dropdowns, just add one more function or (better) change it, so you can pass dependent dropdown as a parameter