I am trying to implement a ajax dropdown on a rails app. When I select the category, the sub category drop down should populate the sub categories according to the category selected. Currently the sub category drop down is getting populated only when I select the category and enter "filter" and goes to the next page. I have the controller method as follows:
controller
def index
#categories = Category.roots.active.all
if (params.keys & ['category_id', 'sub_category_id']).present?
if params[:category_id].present?
#category = Category.active.find params[:category_id]
else
#category = Category.active.find params[:sub_category_id] if params[:sub_category_id].present?
end
end
#root_categories = Category.active.roots
#sub_categories = #category.children.active if params[:category_id].present?
#sub_categories ||= {}
#wanted_equipments = WantedEquipment.Approved.filter(params.slice(:category_id, :sub_category_id)).order("created_at desc").page(params[:page]).per(per_page_items)
end
def fetch_sub_categories
category = Category.active.where(id: params[:category_id].to_i).first
sub_categories = category.present? ? category.children.active : []
render json: sub_categories, status: 200
end
This is the js file
equipment.js
$(document).ready(function($) {
// Fetch sub-categories as per category selected
$("select#category_id, select#wanted_equipment_category_id").selectric().change(function(e){
$.getJSON("/fetch_sub_categories",{category_id: $(this).val(), ajax: 'true'}, function(response){
var options = '';
for (var i = 0; i < response.length; i++) {
options += '<option value="' + response[i].id + '">' + response[i].name + '</option>';
}
if (e.target.id=="category_id"){
$("select#sub_category").html('<option value="">Sub-Category</option>'+options);
var Selectric = $('select#sub_category').data('selectric');
Selectric.init();
}
if (e.target.id=="wanted_equipment_category_id"){
$("select#wanted_equipment_sub_category_id").html('<option value="">Select Sub-Category</option>'+options);
var Selectric = $('select#wanted_equipment_sub_category_id').data('selectric');
Selectric.init();
}
})
})
$(document).ajaxStop(function () {
$('.loader').hide();
});
$(document).ajaxStart(function () {
$('.loader').show();
});
});
This is the index.html.erb file
<%= form_tag filter_wanted_equipments_path, :method => 'get' do %>
<%= select_tag "category_id", options_from_collection_for_select(#categories, "id", "name", params[:category_id]), :prompt=>"Select Category", id: "search_category" %>
<%= select_tag "sub_category_id", options_from_collection_for_select(#sub_categories, "id", "name", params[:sub_category_id]), :prompt=>"Sub-Category" %>
<%= hidden_field_tag( 'category_id', params[:category_id]) if params[:category_id].present? %>
<%= hidden_field_tag( 'sub_category_id', params[:sub_category_id]) if params[:sub_category_id].present? %>
<%= submit_tag "filter-", :name => nil, style: "display:none;", id: 'filter-submit' %>
<% end %>
The routes.rb file
resources :wanted_equipments do
get "/fetch_sub_categories" => 'wanted_equipments#fetch_sub_categories'
collection do
get 'search'
get 'filter'
end
end
http://blog.ashwani.co.in/new/2017/03/06/How-to-use-create-ajax-dropdown-from-database-models-in-Rails.html
Code:
index.html.erb
<div>
<%= f.collection_select :vertical, #verticals, :vertical, :vertical, {:prompt => "Select a Vertical"}, {:class => "dropdown_vertical btn btn-default dropdown-toggle"} %>
<%= f.collection_select :subVertical, #subVerticals, :subVertical, :subVertical, {:prompt => "Select a Sub Vertical"}, {:class => "dropdown_subVertical btn btn-default dropdown-toggle"} %>
</div>
custom.js
$("#search_vertical").on('change', function(){ var listitems = []; $.ajax({ url: "populate_subVerticals", type: "GET", data: {vertical_name: $(this).val()}, success: function(data) { $("#search_subVertical").children().remove(); $("#search_subVertical").append('<option value=>' + "Select a Sub Vertical" + '</option>'); $.each(data,function(key, value) { listitems += '<option value="' + value.subVertical + '">' + value.subVertical + '</option>'; }); $("#search_subVertical").append(listitems); } }) });
Controller
def populate_subVerticals vertical_name = params[:vertical_name] #verticals = Vertical.where(:vertical => vertical_name).select("id","subVertical").all respond_to do |format| format.json { render json: #verticals } end end
routes.rb file
'populate_subVerticals', to: 'verticals#populate_subVerticals'
Here is a piece of code what I have used for populating dynamic drop-down.
index.html.erb --> dd1_id is the static drop-down, once dd1_id is selected, dd2_id drop-down will be populated.
<tr>
<td class="td1"></td>
<td class="td2"><span class="">Drop Down 1:</span></td>
<td><%=f.select :dd1_id, options_for_select(#abc.collect { |abc| [abc.type_name.upcase, abc.type_name.upcase] }, 0), {prompt: "-- Select --"}, { id: 'dd_1_select',:style => "width:300px"} %></td>
</tr>
<tr>
<td class="td1"></td>
<td class="td2"><span class="">Drop Down 2:</span></td>
<td><%=f.select :dd2_id, options_for_select([""]), {}, {:id => "dd_2_select",:style => "width:300px;"} %></td>
</tr>
device.js --> It has a onchange function that checks if the dd1_id drop-down is selected with a value and then triggers a function called update_dd2, that captures the dd1_id value and hits the url specified below in the ajax request.
$('#dd_1_select').on('change', function(){
if ($("#dd_1_select option:selected").val() != ""){
update_dd2();
$('#dd_2_select').val('');
}
});
function update_dd2() {
$.ajax({
url: '/update_dd_2',
data: {
dd1_value: $("#dd_1_select option:selected").val()
},
method: 'GET',
dataType: 'script',
beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))},
success: function(){
console.log("Dynamic dd_2 select OK!");
$('#dd_2_select').prop('disabled',false);
},
failure: function(error){
console.log("AJAX FAILURE: "+error);
alert("Something went wrong, Please try again later.");
},
error: function(error){
console.log("AJAX ERROR: "+error);
alert("Oops! there was an error, Please reload the page and try again.");
}
});
}
}
routes.rb --> The url specified in the ajax request would be redirected to an action
get "update_dd_2" => "abc#update_dd_2", :as => "update_dd_2"
Controller/Action --> Based on the value selected from the first drop-down, It fetches the data required to populate the second drop-down
def update_dd_2
#values = SomeTable.where("id in (?)", params[:dd1_value])
respond_to do |type|
type.js
end
end
dd1_value.js --> The above action would expect a .js file in views
This would render a partial response to the html file and the drop-down would be populated with the appropriate values
$("#dd_2_select").empty().append("<%= escape_javascript(render(:partial => #values )) %>");
Related
I have a rails simple form that creates a product and I have the following three fields inside the form in order to associate the product with the appropriate categories:
<div class="form-group">
<%= f.input :child_category_id, :collection => #categories.order(:name), :label_method => :name, :value_method => :id, label: "Category", :include_blank => "--Select Category--", input_html: { id: "first_dropdown" } %>
</div>
<div class="show_hide">
<%= f.input :subcategory_id, :collection => [] || #subcategory.order(:name), :label_method => :name, :value_method => :id, label: false, :include_blank => "--Select Category--", input_html: { id: "second_dropdown" } %>
</div>
<div class="show_hide_third">
<%= f.input :child_subcategory_id, :collection => [] || #child_subcategory.order(:name), :label_method => :name, :value_method => :id, label: false, input_html: { id: "third_dropdown" } %>
</div>
This is the route:
resources :products, :path => "products/select_item", only: [:select_item] do
get :select_item, on: :collection
end
This is the method inside the controller I have:
def select_item
if params[:child_category_id].present?
#subcategory = Subcategory.where(child_category_id: params[:child_category_id])
render :json => #subcategory.order(:name)
end
if params[:subcategory_id].present?
#child_subcategory = ChildSubcategory.where(subcategory_id: params[:subcategory_id])
render :json => #child_subcategory.order(:name)
end
end
This is the new product and create product methods I have inside the controller:
def new; end
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to root_path }
format.json { render :new, status: :created, location: #product }
flash[:success] = "Product was successfully added."
else
format.html { render :new }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
This is the Javascript I'm using in order to populate the second and third field, according to the selected value:
$('#first_dropdown').on('change', function() {
$.ajax({
url: '/products/select_item/select_item?child_category_id=' + $(this).val(),
dataType: 'json',
success: function(data) {
let childElement = $('#second_dropdown');
if( data.length === 0 ) {
$('.show_hide').hide();
$('.show_hide_third').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>');
});
}
});
});
$('#second_dropdown').on('change', function() {
$.ajax({
url: '/products/select_item/select_item?subcategory_id=' + $(this).val(),
dataType: 'json',
success: function(data) {
let childElement = $('#third_dropdown');
if( data.length === 0 ) {
$('.show_hide').hide();
$('.show_hide_third').hide();
} else {
$('.show_hide').show();
$('.show_hide_third').show();
}
childElement.html('');
data.forEach(function(v) {
let id = v.id;
let name = v.name;
childElement.append('<option value="' + id + '">' + name + '</option>');
});
}
});
});
Everything works just fine and all the fields get populated correctly. The only issue I'm having is that when the form renders again, due to a validation error, the JSON fields show up empty, even though they were correctly populated. How can I keep the JSON form fields populated even if the form returns a validation error?
The issue is that at the point the page loads there are no options in your dropdown to select from; therefore the form cannot select the correct value. You need to update your controller like this:
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to root_path }
format.json { render :new, status: :created, location: #product }
flash[:success] = "Product was successfully added."
else
#categories = Category.all.order(:name)
#subcategory = Subcategory.where(child_category_id: #product.child_category_id).order(:name)
#child_subcategory = ChildSubcategory.where(subcategory_id: #product.subcategory_id).order(:name)
format.html { render :new }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
I'm using the ancestry gem in order to create categories and subcategories(parent, child, sibling). Then I have a select input inside a simple form:
<div class="form-group">
<%= f.input :parent_category_id, collection: #categories, label: "Category", as: :grouped_select, group_method: :children, include_blank: true, input_html: { id: 'first-dropdown' } %>
</div>
I populate the above input with the parent categories:
#categories = Category.where(ancestry: nil)
And when I select a category in the above input the subcategories appear in the input below. If the category has no subcategories then the input is empty.
<div class="form-group">
<%= f.input :category_id, label: "Subcategories", :collection => [], :label_method => :name, :value_method => :id, required: true, input_html: { multiple: true, id: 'second-dropdown' } %>
</div>
$('#first-dropdown').on('change', function() {
$.ajax({
url: 'categories/select_item?category_id=' + $(this).val(),
dataType: 'json',
success: function(data) {
let childElement = $('#second-dropdown');
childElement.html('');
data.forEach(function(v) {
let id = v.id;
let name = v.name;
childElement.append('<option value="' + id + '">' + name + '</option>');
});
}
});
});
Everything works just fine with this setup. But now I would like to add a feature which I don't know how to. I want the second input to be initially hidden. And I want to show the second input if subcategories exist and hide the second input if subcategories don't exist. Any ideas how to implement this?
this could work...
success: function(data) {
let childElement = $('#second-dropdown');
childElement.toggle(Array.isArray(data) && data.length > 0)
...
For some time now, I'm trying to generate a reservation form, where only available rooms are listed for the arrival and departure dates chosen in that same reservation form. Unfortunately, until now I didn't manage to do so.
I set out a question already on my previous attempt, without any success. => https://stackoverflow.com/posts/59083421/edit
I'm now trying to look for alternative ways to get there. For reference, please find my previous attempt below.
Previous attempt: general idea
Tried to send/receive an AJAX. Cannot reach the success stage, and the bug seem lie in the dataType the AJAX call is expecting/I'm sending (JS vs JSON). Unfortunately, I don't know how to solve this.
When I use format.js in my controller, I do not reach the success stage in my ajax call
when I use render json: {:rooms => #rooms} I do not reach my hotels/rooms_availability.js.erb file and subsequently am not able to insert my available rooms.
Previous attempt: code
reservations/new.html.erb
<%= simple_form_for [#hotel, #reservation] do |f|%>
<div class="col col-sm-3">
<%= f.input :arrival,
as: :string,
label:false,
placeholder: "From",
wrapper_html: { class: "inline_field_wrapper" },
input_html:{ id: "start_date"} %>
</div>
<div class="col col-sm-3">
<%= f.input :departure,
as: :string,
label:false,
placeholder: "From",
wrapper_html: { class: "inline_field_wrapper" },
input_html:{ id: "end_date"} %>
</div>
<div class="col col-sm-4">
<%= f.input :room_id, collection: #rooms %>
</div>
<%= f.button :submit, "Search", class: "create-reservation-btn"%>
<% end %>
script for reservations/new.html.erb
<script>
const checkIn = document.querySelector('#start_date');
const checkOut = document.querySelector('#end_date');
const checkInAndOut = [checkIn, checkOut];
checkInAndOut.forEach((item) => {
item.addEventListener('change', (event) => {
if ((checkIn.value.length > 0) && (checkOut.value.length > 0)){
checkAvailability();
}
})
})
function checkAvailability(){
$.ajax({
url: "<%= rooms_availability_hotel_path(#hotel) %>" ,
dataType: 'json',
type: "POST",
data: `arrival=${start_date.value}&departure=${end_date.value}`,
success: function(data) {
console.log('succes')
console.log(data);
},
error: function(response) {
console.log('failure')
console.log(response);
}
});
};
</script>
hotels_controller
def rooms_availability
hotel = Hotel.includes(:rooms).find(params[:id])
arrival = Date.parse room_params[:arrival]
departure = Date.parse room_params[:departure]
time_span = arrival..departure
#unavailable_rooms = Room.joins(:reservations).where(reservations: {hotel: hotel}).where("reservations.arrival <= ? AND ? >= reservations.departure", arrival, departure).distinct
#hotel_cats = hotel.room_categories
#hotel_rooms = Room.where(room_category: hotel_cats)
#rooms = hotel_rooms - #unavailable_rooms
render json: {:rooms => #rooms}
#respond_to do |format|
#format.js
#end
end
def room_params
params.permit(:arrival, :departure, :format, :id)
end
hotels/rooms_availability.js.erb
var selectList = document.getElementById('reservation_room_id')
function empty() {
selectList.innerHTML = "";
}
empty();
<% unless #rooms.empty? %>
<% #rooms.each do |room|%>
selectList.insertAdjacentHTML('beforeend', '<option value="<%= room.id %>"><%= room.name %></option>');
<% end %>
<% end %>
I'm trying to populate one drop down list based on the selection of another drop down list in my rails project. The only difference is that both drop down lists should be populated from one model.
The model is City, and it has name and state.
name is the city name, and state is the state to which the city belongs.
For example, a city object would be :
id = 1
name = LOSANGELES
state = CA
I've checked Rails 4: How to update a collection_select based on another collection_select through AJAX?
and other links but I can't seem to figure out how to populate the city drop down list from the same Model as the first dropdown
Here's my code :
controllers/city_stats_controller.rb :
def city_stats
#state = params[:state]
#cities = City.select(:name).all.where(:state => state)
respond_to do |format|
format.json { render :json => #cities }
end
end
views/city_stats/city_stats.html.erb :
<div class="row">
<label>State <span>*</span></label>
<%= collection_select :city, :state, City.select(:state).uniq.order('state ASC'), :id, :state, {:prompt => 'Select a State'}, {id: 'state', multiple: false} %>
<label>City <span>*</span></label>
<%= collection_select :city, :name, [], :id, :name, {:prompt => 'Select a City'}, {id: 'name', multiple: false} %>
</div>
<script type="text/javascript">
$(document).ready(function() {
$("#state").on('change', function(){
$.ajax({
url: "/admin/city_stats",
type: "GET",
data: {state: $(this).val()},
success: function(data) {
$("#city").children().remove();
// Create options and append to the list
var listitems = [];
$.each(data,function(key, value) {
listitems += '<option value="' + key+ '">' + value + '</option>'; });
$("#city").append(listitems);
console.log(listitems);
}
})
});
});
</script>
Any help is much appreciated!
change your city_stats method like. This method should return id and name
def city_stats
#state = params[:state]
#cities = City.where(:state => state).select(:id, :name)
respond_to do |format|
format.json { render :json => #cities }
end
end
Change your each function in ajax call like this.Since data in response is array of object we are using value.id, value.name .
$.each(data,function(key, value) {
listitems += '<option value="' + value.id + '">' + value.name + '</option>';
});
Hello guys i am trying attempt a dynamic select here. as soon as i select the customer his total value in the bill should come and get displayed in the text field tag.
the view
jQuery(document).ready(function(){
jQuery(".customerid").bind("change", function() {
var data = {
customer_id: jQuery(".customerid :selected").val()
}
jQuery.ajax({
url: "get_cust_bill",
type: 'GET',
dataType: 'script',
data: data
});
});
});
</script>
<div class ="customerid"><%= f.label :customer_id %>
<%= f.collection_select :customer_id, Customer.all, :id, :name, options ={:prompt => "-Select a Customer"}, :class => "state", :style=>'width:210px;'%></div><br />
<div class ="customerbill">
<%= f.label :total_value, "Total Value" %>
<%= render :partial => "customerbill" %>
js.erb file
jQuery('.customerbill').html("<%= escape_javascript(render :partial => 'customerbill') %>");
the customerbill partial
<% options = []
options = #cust_bill.total_value if #cust_bill.present? %>
<%= text_field_tag "total_value", options %>
in contoller
def get_cust_bill
#cust_bill = CustomerBill.find_all_by_customer_id(params[:customer_id]) if params[:customer_id]
end
I feel the problem lies in the partial, the way i am calling the options so can anyone guide me how to get the value in text field??? thank in advance.
From what I understand, total_value text field does not show anything. Could you try to output the value of options and check if it always has a value? I suggest you check out the documentation for the text_field_tag. Basically, it accepts three variables like this:
text_field_tag(name, value = nil, options = {})
i was using getJSON method....and i feel that can be used here. hope the followng works.
jQuery(document).ready(function()
{
jQuery(".customerid select").bind("change", function() {
var data = {
product_id: jQuery(this).val()
}
jQuery.getJSON(
"/controller_name/get_cust_bill",
data,
function(data){
var result = "";
res = parseFloat(a[1]);
jQuery('.price input').val(res);
});
});
});
controller
def get_cust_bill
#cust_bill = CustomerBill.find_all_by_customer_id(params[:customer_id]).map{|p| [p.price]} if params[:customer_id]
respond_to do |format|
format.json { render json: #cust_bill }
end
end
so no need of calling js. erb partial you can simply have
<div class = "price"><%= f.input :price, :label => 'Price', :input_html => { :size => 20} %></div><br/>
all the best :)