Rails, Select2 and Ransack return emprty array - ruby-on-rails

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.

Related

Load model association dynamically in Rails form before submit

So I'm not sure how to achieve this or if it's even possible and having a tough time searching for similar examples/answers. But is there a way to dynamically load a model association within a form before you submit it, like without knowing what the instance variable would be ahead of time?
I have models as follows:
Property
has_many :owners, inverse_of: :property
has_many :orders
Owners
belongs_to :property
Orders
belongs_to :property
has_many :owners, through: :property
In my orders.new form I have select boxes to choose a property to create the association for the new order (using Select2 plugin):
<%= bootstrap_form_for(#orders, ...) do |f| %>
...
<%= f.select :property_id, options_from_collection_for_select(Property.all, "id", "full_address"),
{ label: "Property:", include_blank: true }, { id: "orderPropSelect", data: { placeholder: "Select a Property"} } %>
...
<% end %>
So when someone SELECTS a property for the new order, is there a way I can load what owners that property already has associated to it before the form is submitted? Even just being able to see what owners are already there is okay (being able to edit them would be even better but I realize thats probably more complex)
The following code of course doesn't work but looking for something along the lines of:
<%= f.static_control label: "Property Owners" do %>
<% :property_id.owners.each do |owner| %>
<%= owner.name %>
<% end %>
<% end %>
I've tried variations of fields_for but I don't know how to tell the nested fields to be based off what is chosen in the above select (of course loading different owners based on what property is chosen. The errors that I get are undefined method owners for nil:NilClass which are appropriate because I know I'm not telling rails where to look correctly.
So is this possible and if so, how would I achieve this?
(I use bootstrap forms gem incase anyone is wondering about form syntax. I also have cocoon loaded for other forms so if there's a way to use that then I'm not opposed.)
Update Working code, slightly modified for extraneous conditions.
$("#orderPropSelect").off().on('change', function() {
var id = $(this).val();
console.log(id);
if (id !== '') {
$.ajax({
url: '/properties/' + id + '/owners',
dataType: "json",
success: function (data) {
owners_html = '';
$.each(data['owners'], function () {
owners_html += '<p>' + this + '</p>';
});
if (owners_html === '') {
$("#propOwnersShow").html('<p>No owner added yet.</p>');
} else {
$("#propOwnersShow").html($(owners_html));
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
console.log(errorThrown);
}
});
} else {
$('#propOwnersShow').html('Please select a Property below to view its associated owners.')
}
});
You need to make sure it matches your underlying routes & stuff, and probably handle the case where there aren't owners, so to hide the owners div. If you want to do more complex stuff you could instead of .pluck build a better array/hash with id's that you can then also use to build elements that you can interact with (e.g. remove them from the list)
# your property controller
before_action :allowed, only [:owners]
def owners
owners = Property.find(params[:id]).owners.pluck(:name)
respond_to |format|
format.json { render json: { owners: owners, success: true } }
end
end
def allowed
# logic to define if this user is allowed to request this data, if you have devise, it could be
user_signed_in? && request.xhr?
end
# your routes.rb
get "properties/:id/owners", to: "properties#owners", as: "property_owners"
# or if you have resources
resources :properties do
member do
get :owners
end
end
# js file
$("#property_id").off().on('change', function() {
var id = $(this).val();
$.ajax({
url: '/properties/'+id+'/owners',
dataType: "json",
success: function(data) {
owners_html = '';
$.each(data['owners'], function() {
owners_html += '<p>'+this+'</p>';
});
$("selector_where_you_want_to_show_owners").html($(owners_html));
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
console.log(errorThrown);
}
});
}
UPDATE:
You can prevent the issue with no Owners by using find_by instead and making sure that you always return an [], this way simplifying the logic on the front-side as well.
# on your controller instead use:
def owners
# .find_by will return "nil" if no properties are found, which
# then we can leverage with .try. .try will "try" calling those
# chained methods without raising an error. It usually returns
# "nil" if the parent is "nil" but .try with .pluck though will
# always return an [] no matter what the ancestors returned.
owners = Property.find_by(id: params[:id]).try(:owners).try(:pluck, :name]
# actually looking at your relationships it seems you could instead
# .where, since that will return an empty array, like so:
# owners = Owner.where(property_id: params[:id]).pluck(:name)
# this only does one database query where the other one does 2
# here we're simply making sure we add something to the array so
# that then on the front-end you can always deal with an array
# without worrying with the "no properties found". .blank? will
# return "true" if the value is nil, empty string, empty hash or
# empty array which works fine for this. So if there's no value in
# the array we add the "no owners" string.
owners << 'No owner added yet.' if owners.blank?
respond_to |format|
format.json { render json: { owners: owners, success: true } }
end
end
# And then since you'll always be returning an array you no longer
# have to worry about an empty array and your ajax function will
# look like this:
$.ajax({
url: '/properties/' + id + '/owners',
dataType: "json",
success: function (data) {
owners_html = '';
$.each(data['owners'], function () {
owners_html += '<p>' + this + '</p>';
});
$("#propOwnersShow").html($(owners_html));
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
console.log(errorThrown);
}
});
Hope this helps

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.

Remember dynamic select options in 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!

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

Rails mongoid solr grouping remote ajax data in Select2: Optgroup appearing multiple times when I search for a child

I'm using Select2 with Rails and Mongoid and I have Sunspot Solr on the server side to do the searching. What I'm trying is to filter the data from mongo by solr, get that data by ajax request and group the results according to parent-child hierarchy. What I can't figure out is when I search for a child and if there are multiple child results, the parent category is occuring multiple times. Please read below for more info:
Here is my data in Mongo DB
{ "_id" : ObjectId("5209eb465a721ae827c661de"), "title" : "Bina Kanalizasyon Tesisatı", "parent" : "Su Tesisatçılığı", "path" : "sutesitati/kanalizasyon" }
{ "_id" : ObjectId("5209eb465a721ae827c661df"), "title" : "Daire Temiz Su Tesisatı", "parent" : "Su Tesisatçılığı", "path" : "sutesisati/temizsu" }
{ "_id" : ObjectId("5209eb465a721ae827c661e0"), "title" : "Musluk Tamiri", "parent" : "Su Tesisatçılığı", "path" : "sutesitati/musluktamiri" }
Here is my Rails Model
class Category
include Mongoid::Document
include Sunspot::Mongoid2
searchable do
text :title
end
field :title, :as => :title_textp
field :parent
field :path
end
Here is my Rails Controller
class CategoriesController < ApplicationController
respond_to :html, :json
def list_styles
search = Category.search do
fulltext params[:q]
end
search = Category.search { keywords params[:q]; paginate :page => params[:page], :per_page => params[:page_limit] }
#results = search.results
#total_lines = search.total
#categories = #results
respond_with #categories
end
end
Here is my Haml View
= f.label :category
= f.hidden_field :category, class: 'input-block', placeholder: "Lütfen almak istediğiniz hizmet türünü seçiniz"
And Here is my Javascript that I'm trying to figure out
$('#itinerary_category').select2({
minimumInputLength: 3,
ajax: {
url: "/categories/list_styles",
dataType: 'json',
quietMillis: 100,
data: function (term, page) {
return {
q: term,
page_limit: 10,
page: page,
};
},
results: function (data) {
var results = [];
$.each(data, function(index, item){
results.push({
text: item.parent,
children: [{id: item._id, text:item.title}]
});
});
return {
results: results
};
}
}
});
When I run the server and make a search its working ok for group with one item
However When I make a search that displays multiple items the group name("Su Tesisatı") is displayed twice too.
I don't want the group name to be displayed twice and I can't figure out how to make the change. I want a list like
Su Tesisatçılığı
Daire Temiz Su Tesisatı
Bina Kanalizasyon Tesisatı
I'm not good at js and could not figure out how to solve it. Could anyone please help me? I think the example I've provided also adds up to discussion about Grouping remote data in select2 and clarly states how to use select2 with rails+mongoid+solr+ajax. If anyone could clear this then this will make select2 more powerful because there is almost no clear example on the web for making it work in grouping with ajax remote data.
Any help would be greatly appreciated.
in this code you are creating a parent item per child:
$.each(data, function(index, item){
results.push({
text: item.parent,
children: [{id: item._id, text:item.title}]
});
});
instead, you should group by parent:
var hashtable={};
var results=[];
$.each(data, function(index, item){
if (hashtable[item.parent]===undefined) {
hashtable[item.parent]={text:item.parent, children:[]};
results.push(hashtable[item.parent]);
}
hashtable[item.parent].children.push({id:item._id,text:item.title});
});

Resources