Remember dynamic select options in Rails - ruby-on-rails

Models:
User create Event, and each Event need a Court location, then a House location, the value of House depends on the value of Court, for example:
select Court A, returns House as A1, A2, A3
select Court B, returns House as B1, B2, B3
In Event Controller form:
def new
#event = current_user.events.build(event_params)
#courts = Court.all
#houses = House.where("court_id = ?", Court.first.id)
end
def edit
#event = Event.find(params[:id])
#courts = Court.where(:id => #event.court_id)
#houses = House.where(:id => #event.house_id)
end
In View form:
<%= f.label 'Event Location' %>
<%= f.select :court_id, options_for_select(#courts.collect { |court| [court.name, court.id] }, 1), {}, { id: 'courts_select' } %>
<%= f.select :house_id, options_for_select(#houses.collect { |house| [house.name, house.id] }, 0), {}, { id: 'houses_select' } %>
Javascript:
$(function() {
return $(document).on('change', '#courts_select', function(evt) {
return $.ajax('/events/update_houses', {
type: 'GET',
dataType: 'script',
data: {
court_id: $("#courts_select option:selected").val()
},
error: function(jqXHR, textStatus, errorThrown) {
return console.log("AJAX Error: " + textStatus);
},
success: function(data, textStatus, jqXHR) {
return console.log("Dynamic court select OK!");
}
});
});
});
Now this works fine when the user create the event, but when they want to edit, the Court and House Select only shows 1 value. It's because in the edit controller the where() only returns 1 value. How can I change my edit controller code so that at edit form, it returns the a list of choices and remembers the user's choice

For starters, you want to supply all Courts and the corresponding Houses for the currently-selected Court.
def edit
#event = Event.find(params[:id])
#courts = Court.all
#houses = House.where(:court_id => #event.court_id)
end
Then you want your selects to select the proper value from the dropdown. Instead of:
<%= f.select :court_id, options_for_select(#courts.collect { |court| [court.name, court.id] }, 1), {}, { id: 'courts_select' } %>
Something like:
<%= f.select :court_id, options_for_select(#courts.collect { |court| [court.name, court.id] }, #event.court_id), {}, { id: 'courts_select' } %>
... rinse and repeat for the House select
Hope that helps!

Related

Rails, Select2 and Ransack return emprty array

I have a City Model:
class Geo::City < Smth
self.table_name = 'cities_table'
has_many :streets, class_name: "Geo::Street", primary_key: :geo_code, foreign_key: :geo_code
The aim is, to search all streets in the city and print them as a search result. In my controller I have a filter method as follows:
def filter
#city_list = city_list
params[:q] ||= {}
params[:q][:ags8_id_eq] = #city_list[0][1]
if params[:q].present? && params[:q][:geo_code_eq].present?
#city = Geo::City.find_by(geo_code: params[:q][:geo_code_eq])
#collection = [["#{#city}", "#{params[:q][:geo_code_eq]}"]]
end
#streets = #city.streets.limit(15)
#q = #streets.ransack(params[:q])
end
I also have a search_city method as follows:
def search_city
render json: city_list.select {|city| city.first.include?(params[:q][:geo_code_eq])}
end
And a private method city_list which I am calling in methods above:
def city_list
all_cities = Geo::City.order('city_name').reduce([]) {|cities, c| cities << [c.name, c.geo_code]}
I had to change all the method names,cause it's for work, so the_city list is kind of an example and it also works fine, I assume, this is not the problem. I also have a js-file for select 2.
(function() {
$(function() {
return $('.city-select2').select2({
ajax: {
url: "/filter/search_city",
dataType: 'json',
delay: 250,
data: function (params) {
return {
q: {
geo_code_eq: params.term
}
};
},
processResults: function(data) {
return {
results: $.map(data, function(obj) {
return {
id: obj[1],
text: obj[0]
};
})
};
},
cache: true
},
minimumInputLength: 2,
placeholder: "City",
tags: false,
tokenSeparators: [","],
selectOnClose: true,
allowClear: true
});
});
}).call(this);
It seems to be working, cause when I type city name it finds the city with the letters I typed. What happens after I submit select form is that I just get an empty array, so basically like this:
The url looks the following:
http://localhost:3000/filter/search_city?q%5Bgeo_code_eq%5D=16071003&commit=Search
My view does this:
= search_form_for #q, url: search_city_path do |f|
.columns.is-centered{style: 'padding-top: 100px;'}
.column.is-half
= f.select(:geo_code_eq, options_for_select(collection: #collection.present? ? #collection : [], input_html: { class: "city-select2 select-input" })
= f.submit "Search"
What is wrong? How do I get streets in the cities printed? There might be some spelling mistakes in the code, as I had to replace names, this won't be the reason for my empty array. Also, in the url there is only B selected, maybe that the reason? But it also finds the geo_code, so it should be capable of giving the streets back linked to geo_code, as geo_code is also the part of streets_table..
Main reason for the array was the path in the view. Should have been filter_path ans not the search_city_path, as it was the same path my select2 was getting data from, redirection there on submit made no sence.

Filter select fields based on previous select fields values

I'm new at RoR and I'm having a trouble in my app. The problem consists on filter a select field named "Solution", based on the others select fields above it.
Now, what the app do is to retrieve all information from BD about Area, Region, Associated, Solution and populate the select fields with these data. But the user wants that, when an area, a region and an associated is selected by the user, only the solutions about that associated in that region on that area should be shown.
Edit:
I'm almost there! I've made many changes in my app. The select fields are populated by controller action new and the function "populate_selects", which is called by the parameter before_action :popula_selects, only: [:new, :edit]. A new function was created in order to be called by AJAX and upgrade the "Solution" field:
Atendments_Controller < ApplicationController
before_action :populate_selects, only: [:new, :edit]
def new
#atend = atendment.new
end
def update_solution #AJAX
#solutions = atendment.joins(:solution).where("atendment_area_id = ? and atendment_region_id = ? and atendment_assoc_id = ?", params[:atendment_area_id], params[:atendment_region_id], params[:atendment_assoc_id])
respond_to do |format|
format.js
end
end
private
def populate_selects
#atendment_area = atendmentArea.where(status: true, user_id: current_user.id)
#atendment_region = atendmentRegion.where(status: true, user_id: current_user.id)
#atendment_assoc = atendmentRegionAssoc.where(status: true, assoc_id: current_user.entidade_id).where(atendment_region_id: #atendment_region.map(&:atendment_region_id))
#solutions = atendment.joins(:solution).where("atendment_area_id = ? and atendment_region_id = ? and atendment_assoc_id = ?", params[:atendment_area_id], params[:atendment_region_id], params[:atendment_region_assoc_id])
end
end
Below, the _form.html.erb code from view:
<div class="atendment-form">
<%= form_for :atendment, url: {action: "new"}, html: {method: "get"} do |f| %>
<div class="col-xs-6">
<%= f.select :atendment_area_id, options_for_select(#atendment_area.collect { |c| [ c.atendment_area.name, c.id ] }, 1), {:prompt=>"Área"}, { :class => 'form-control', :required => true, id: 'atendment_atendment_area_id' } %>
</div>
<div class="col-xs-6">
<%= f.select :atendment_region_id, options_for_select(#atendment_region.collect { |c| [ c.atendment_region.name, c.id ] }, 1), {:prompt=>"Região"}, { :class => 'form-control', :required => true, id: 'atendment_atendment_region_id' } %>
</div>
</div>
</div>
<div class="field">
<%= f.select :atendment_assoc_id, options_for_select(#atendment_assoc.collect { |c| [ c.atendment_region.name, c.id ] }, 1), {:prompt=>"Associado"}, { :class => 'form-control', :required => true, id: 'atendment_atendment_assoc_id' } %>
</div>
<div class="field">
<%= f.select :solution_id, options_for_select(#solutions.collect { |solution| [solution.name, solution.id] }, 0), {:prompt=>"Solução"}, { :class => 'form-control', :required => true, id: 'atendment_solution_id' } %>
</div>
</div>
Route to the new function:
resources :atendments do
collection do
get :update_solution
end
end
AJAX function which calls the "update_solution" and reset solution field's value (app/assets/javascript/atendment.js.coffee):
show_solutions = ->
$.ajax 'update_solution',
type: 'GET'
dataType: 'script'
data: {
atendment_area_id: $("#atendment_atendment_area_id").val()
atendment_region_id: $("#atendment_atendment_region_id").val()
atendment_assoc_id: $("#atendment_atendment_assoc_id").val()
}
error: (jqXHR, textStatus, errorThrown) ->
console.log("AJAX Error: #{textStatus}")
success: (data, textStatus, jqXHR) ->
console.log("OK!")
$(document).ready ->
$('#atendment_atendment_assoc_id').on 'change', ->
show_solutions()
So, I've created a .coffee file to render the partial that will return a new value to the "solution" field "option" tag
(app/views/atendment/update_solution.coffee):
$("#atendment_solution_id").empty()
.append("<%= escape_javascript(render :partial => 'solution') %>")
And, the last but not least, the partial containing the html code for the "option" tag mentioned above (app/views/atendments/_solution.html.erb):
<option value="<%= solution.id %>" selected="selected"><%= solution.nome %></option>
For any reason, the AJAX function doesn't print nothing on console (nor error neither success), but it calls the update_solution.coffee file. The point is, it doesn't update the option value due an error (500 internal server error). I don't know what am I doing wrong. If anybody could help me, I appreciate it.
I would do this with JS, can think any other way.
A function called by onchange that change the display attribute from each field that you need to hide or show.
I solved this with the following code:
assets/js/atendments.js
I changed the code because the last one had many bugs.
function getAssociated(){
var aau_id = $("#atendment_area_user_id").val()
var aru_id = $("#atendment_region_user_id").val();
$.getJSON("/controllers/atendments_controller/getAssociated/"+aru_id,
function ( callback ) {
if (callback != "error"){
var assoc = document.getElementById("atendment_region_associated_id");
while (assoc.firstChild) {
assoc.removeChild(assoc.firstChild);
}
var i = Object.keys(callback).length -1;
$("#atendment_region_associated_id").append("<option value=''>Associated</option>");
while (i >= 0) {
$("#atendment_region_associated_id").append("<option value='"+callback[Object.keys(callback)[i]]+"'>"+Object.keys(callback)[i]+"</option>");
i--;
}
}
});
get_solution_type();
}
function get_solution_type() {
var ara_id = $("#atendment_region_associated_id").val();
$.getJSON("/controllers/atendments_controller/getSolution/"+ara_id,
function ( callback ) {
if (callback != "error"){
var sol = document.getElementById("atendment_solution_id");
while (sol.firstChild) {
sol.removeChild(sol.firstChild);
}
var i = Object.keys(callback).length-1;
while (i >= 0) {
$("#atendment_solution_id").append("<option value='"+callback[Object.keys(callback)[i]]+"'>"+Object.keys(callback)[i]+"</option>");
i--;
}
}
});
var aau_id = $("#atendment_area_user_id").val();
$.getJSON("/controllers/atendments_controller/getType/"+aau_id,
function ( callback ) {
if (callback != "erro"){
var type = document.getElementById("atendment_type_id");
while (type.firstChild) {
type.removeChild(type.firstChild);
}
var i = 0;
while (i < (Object.keys(callback).length)) {
$("#atendment_type_id").append("<option value='"+callback[Object.keys(callback)[i]]+"'>"+Object.keys(callback)[i]+"</option>");
i++;
}
}
});
}
The $.getJSON performs ajax request to the controller that responds with JSON and update the select fields option tags.
controllers/atendments_controller
I just retrieve the data from DB and return as JSON
def getAssociated
aru_id = params[:atendment_region_user_id]
aras = AtendmentRegionAssociated.where("SQL here")
if aras.present?
render :json => aras.to_json
else
render :json => "error".to_json
end
end
def getSolution
ara_id = params[:atendment_region_associated_id]
sol = Solution.where("SQL here")
if sol.present?
render :json => sol.to_json
else
render :json => "error".to_json
end
end
def getType
aau_id = params[:atendment_area_user_id]
type = AtendmentType.where("SQL here")
if type.present?
render :json => type.to_json
else
render :json => "error".to_json
end
end
Update the routes and put the javascript functions in select fields onchange property. Now everything is working fine :D

select2 initSelection for redisplayed the form

I'm Using Select2-rails '3.5.3, I can do search with remote data and save it, the problem is how I can restore the selected text (for example if user press edit), problem: initSelection is not fired when form loaded with edit
below is my code
hotelcheck.coffee
$('.select2-autocomplete').each (i, e) ->
select = $(e)
options = {
multiple: false
width: "98%"
placeholder: "Type Hotel name"
minimumInputLength: 3
}
options.ajax =
url: select.data('source')
dataType: 'json'
type: "GET"
quietMillis: 250
# input untuk program
data: (term, page) ->
{ q: term,
page: page,
per: 25 }
results: (data) ->
{ results: $.map(data, (item) ->
{
text: item.name
id: item.id
}
) }
initSelection: (element, callback) ->
id = $(element).val()
if id != ''
$.ajax('/hotels/' + id + '.json', dataType: 'json').done (data) ->
selected =
id: element.val()
text: data.name
callback selected
return
return
options.dropdownCssClass = 'bigdrop'
select.select2 options
form to show hidden field, I save the content to hotel_id
<%= f.hidden_field :hotel_id, data: { source: search_name_hotels_path }, class: "select2-autocomplete", :value => "#{f.object.hotel_id unless f.object.new_record? || f.object.hotel_id.nil? }" %>
hotel controller for ajax send the value
def show
#hotel = Hotel.find(params[:id])
puts "running fill name"
respond_to do |format|
format.json { render json: #hotel }
end
end
source data = hotel table with field id, name
client table = order with field hotel_id
From this page, it looks like initSelection should NOT be in options.ajax, but just in options. I would try to copy the example they have and edit it to your needs. OR update to 4.0.0, which seems to be much easier.

simple_form select collection populated by AJAX call

In my view I have a simple form with two select lists:
<%= simple_form_for #job, url: jobs_path do |f| %>
<%= f.input_field :types, as: :select, collection: #types, id 'types-select' %>
<%= f.input_field :subtypes, as: :select, collection: #subtypes %>
<% end %>
When a user selects an option from the first list, the second list below should be populated with values from the database based on the above selection.
For this reason, I am making ajax request when a user selects an option from the first list:
$('#types-select').change(function(){
$.ajax({
url: '/subtypes',
dataType: 'json',
type: 'GET',
data: {
type_id: this.value
},
success: function(data) {
console.log(data);
}
});
});
Controller looks like this:
class SubtypesController < ApplicationController
respond_to :json
def index
#subtypes = Type.find(params[:type_id]).subtypes
render json: #subtypes
end
end
At this point how can I fill up the second select with options from #subtypes?
You can populate second dropdown within success callback. Make sure #subtypes is returned in proper json format too.
Controller:
def index
#subtypes = Type.find(params[:type_id]).subtypes
render json: #subtypes.map { |item| { value: item.value } }
end
JS:
$.ajax({
url: '/subtypes',
dataType: 'json',
type: 'GET',
data: {
type_id: this.value
},
success: function(data) {
// Populate second dropdown here
var output = '';
$subtypes.empty().append(function() {
data.forEach(function(item) {
output += "<option>" + item.value + "</option>"
});
return ouput;
});
}
});

acts_as_taggable_on and select2 returning weird results in Active Admin

So I have been playing around with acts_as_taggable_on in active admin, and for the most part everything is working as expected.
However, whenever I search for tags, and add an existing tag to a model, it seems to save it as the ID, rather than as the name. Creation of new tags returns the name fine, and when I go to edit the object again the tags remain tagged by the name. But when I try and add another tag, one that already exists in the database, it returns the name in the form, and seems to save OK, but when I go back to edit the onject again the tag shows up as an ID, rather than the name.
In admin/gift.rb:
controller do
def autocomplete_gift_tags
#tags = ActsAsTaggableOn::Tag
.where("name LIKE ?", "#{params[:q]}%")
.order(:name)
respond_to do |format|
format.json { render json: #tags , only: [:id, :name], root: false }
end
end
end
In tag-autocomlete.js:
$(document).ready(function() {
$('.tagselect').each(function() {
var placeholder = $(this).data('placeholder');
var url = $(this).data('url');
var saved = $(this).data('saved');
$(this).select2({
tags: true,
placeholder: placeholder,
minimumInputLength: 1,
initSelection: function(element, callback) {
saved && callback(saved);
},
ajax: {
url: url,
dataType: 'json',
data: function(term) {
return {
q: term
};
},
results: function(data) {
return {
results: data
};
}
},
createSearchChoice: function(term, data) {
if ($(data).filter(function() {
return this.name.localeCompare(term) === 0;
}).length === 0) {
return {
id: term,
name: term
};
}
},
formatResult: function(item, page) {
return item.name;
},
formatSelection: function(item, page) {
return item.name;
}
});
});
});
And in my _gift_form.html.erb:
<%= f.input :tag_list, label: "Tags", input_html: { data: { placeholder: "Enter tags", saved: f.object.tags.map{|t| {id: t.name, name: t.name}}.to_json, url: autocomplete_gift_tags_path }, class: 'tagselect' } %>
Can't work out why the new ones are working, but the existing tags are not.
change this:
respond_to do |format|
format.json { render json: #tags , only: [:id, :name], root: false }
end
to this:
respond_to do |format|
format.json { render :json => #tags.collect{|t| {:id => t.name, :name => t.name }}}
end

Resources