RoR live-search (text_field_with_auto_complete) submit - ruby-on-rails

I have a "Movies" and a "Actors" table and "Casts" as join-model. To be more specific "Casts" has movie_id, actor_id and rolename.
I want in "Movies" form to add a live search to search through actors and a "rolename" text_field and save those to "Casts".
I don't know if text_field_with_auto_complete is the right choice but i prefer not to use much javascript because i am not familiar with it.
I've been searching all over the internet to find something similar to this without any result.
I've manage to get it working with "#actors.each do" but it makes a very long list.

It's not a plugin, but with a little jQuery magic, you can make use of http://github.com/chadisfaction/jQuery-Tokenizing-Autocomplete-Plugin. The nice thing about this is that since it is pure JS in its implementation, you can create the AJAX call yourself in Rails and only display what you want. It even allows you to add a stylesheet to the dropdown if you want to make it more Facebook like. In your controller, add a function so the AJAX call will return a list of rows in JSON:
def taglist
tags = []
sql = "SELECT id,name ... LIMIT 15" # Enter SQL here to produce a possible result set
result = ActiveRecord::Base.connection.execute(sql)
# Iterate over the hash values and push them into an array
result.each { |field| tags.push( {"id" => field[0], "name" => field[1]} ) }
result.free
render :json => tags, :layout => false
end
In the view, add the following code:
<%= javascript_include_tag 'jquery.tokeninput' %>
<%= stylesheet_link_tag 'token-input-facebook' %>
<script type="text/javascript">
jQuery(document).ready(function () {
jQuery("#actors_role").tokenInput("/actors/rolesearch", {
allowNewValues: false,
canCreate: false,
hintText: "Enter the actor's name or role they played",
});
});
</script>

See text_field_with_auto_complete inside form_for
In the auto_complete controller action, make sure your SQL query is restricting the actors names using the passed param.

Related

How to add a search parameter to an existing Ransack query

I have a search form on a page, that allows a user to filter a list on multiple conditions. Now I'd like to add quick links on top of the list to apply scopes. I tried it like this:
= link_to "Visited", q: {"activated" => true}
While this filters the list to show only activated items, it also resets the search query. In other words, it doesn't remember what was already filtered in the form.
Is there a way to adapt #q so that I can add this "activated" => true to the hash of required filters?
Assuming you're only using the :q param to filter, you could aggregate that.
= link_to "Visited", q: (params[:q] || {}).merge(activated: true)
I don't think you can because if you follow a link you are not submitting the form therefore the parameters are not going to be submitted.
Passing the params in your link to will send the params if any exist:
= link_to "Visited", q: {"activated" => true, "your_params" => params}
This will only work if the form has been submitted once though, otherwise the params would be empty.
EDIT
I assume that the fields on your forms are populating if there is a value.
For example,
<%= text_field_tag(:email, if !params["email"].nil? then params["ip_email"] end) %>

How get the value of a collection_select within the same html.erb form itself

I have a form with this collection_select
<%= collection_select :bmp, :bmpsublist_id,
Bmpsublist.where(:bmplist_id => #bmp.bmp_id), :id,
:name,{ :required => false,
:selected => #bmp.bmpsublist_id, } %>
I would like to be able to get the value of this collection_select so that lower down in the same form, I can check to see which list I should use when displaying another collection_select
Something like this partial pseudocode here:
if earlier result == 2 then
use this list: Irrigation.where(:id != 8)
else
use this other list: Irrigation.all
and they would be updating the collection_select:
<%= collection_select :bmp, :irrigation_id, the_chosen_list_from_above, :id, :name,
{:prompt => 'Select Irrigation Type'}, {:required => true} %>
How can I do that?
Based on what you've asked, there are two ways to query and apply the value of the collection: static and dynamic.
Static occurs at the time that the ERB view is rendered, and this will apply the logic at the time that the page is initially rendered and loaded. Dynamic occurs after the page is loaded, and as the user interacts with the elements on the page. Which approach you choose to go with depends entirely on your application's design and intended level of interaction with the user.
Static Detection
You're already specifying the selected item in the initial collection_select, so you can reuse that in your later code. Try this, based on your pseudocode example:
<% if #bmp.bmpsublist_id == 2 %>
<% irrigation_list = ["Sprinkle", "Furrow/Flood", "Drip", "Furrow Diking"] %>
<% else %>
<% irrigation_list = ["Sprinkle", "Furrow/Flood", "Drip", "Furrow Diking", "Pads and Pipes - Tailwater Irrigation"] %>
<% end %>
<%= select :bmp, :irrigation_id, options_for_select(irrigation_list),
{ :prompt => 'Select Irrigation Type'}, { :required => true } %>
Why will this work? The :selected option for the initial collection_select is where you provide which option will be initially chosen. Since this value is typically taken from the model value, it's supplied in a separate param from the actual collection values. So, it's queued up and ready for you, simply by virtue of sticking to the Rails conventions.
The subsequent select builds the HTML <select> element and uses the options_for_select to turn the array of options into HTML <option> elements. This way, you can use the variable list of options to select from, based on which element from the original collection_select was chosen.
Best thing of all: with the static approach, you don't have to drop into Javascript (or jQuery) to do this; it gets rendered directly by the ERB template (or the HAML template, if that's your bag).
Dynamic Detection
If you actually wanted dynamic behavior, you can drop into Javascript / jQuery and get it done. You can create your "Irrigation Types" select just like with the static approach (above), except that you initialize it with all of the options, like this:
<%= select :bmp, :irrigation_id,
options_for_select(["Sprinkle", "Furrow/Flood", "Drip", "Furrow Diking", "Pads and Pipes - Tailwater Irrigation"]),
{ :prompt => 'Select Irrigation Type'}, { :required => true } %>
Then, edit the Javascript source associated with your view (let's call it Product). Open the app/assets/javascripts/product.js (if you use CoffeeScript, it's the product.coffee file in the same directory).
Edit that Javascript file to include this code:
function OnProductEditForm() {
// Edit the selectors to match the actual generated "id" for the collections
var bmp_collection = $("#product_bmp");
var drip_collection = $("#product_irrigation_type");
var drip_option = drip_collection.find("option")[2];
function select_available_drip_options() {
var value = bmp_collection.val();
if (value == 2) {
drip_option.attr("disabled", "disabled");
} else {
drip_option.removeAttr("disabled");
}
}
bmp_collection.change(function() {
select_available_drip_options();
});
select_available_drip_options();
}
This identifies the HTML element of the collection and installs a change event handler. You'll need to verify the collection element's id, as per the code comment, and the rest happens from there. When the collection is changed (a new value is chosen), the event handler will hide or show the third select <option> (specified as find("option")[2]), as appropriate for the #product_bmp selection.
Next, in the app/views/products/_form.html.erb, include this at the end of the file:
<script>
jQuery(document).ready(OnProductEditForm);
// Uncomment the next 2 lines for TurboLinks page refreshing
//jQuery(document).on('page:load', OnProductEditForm);
//jQuery(document).on('page:restore', OnProductEditForm);
</script>
This will automatically load the OnProductEditForm method when the page loads, and will result in the afore-mentioned event handler getting installed. Note that the last 2 lines are necessary if you have TurboLinks enabled, as TurboLinks initiates events for page loading independently of the standard $(document).ready.
And that's all there is to it. Adding dynamic behavior is just that easy!
You're gonna have to use some javascript (jquery and ajax, I would suggest). When the value of the first select change (jquery), it requests (ajax) the collection (passing the current value selected) to a controller action that returns the collection that should be used. With the collection returned, you populate the options (jquery) for the second select. It's not quite simple, but if you ever did something like that, you shouldn't have problems. If never did, do some research about it... it's quite useful and improve user experience a lot!

simple_form select collection populated by AJAX

I thought this would be fairly easy, but I'm not finding any help by Googling.
I have a form (simple_form) with numerous inputs with select lists (collections) that are populated from the database, so many it is slowing down the initial page load. I thought I could speed it up by only populating those drop down lists as the user selects them using Ajax. Is there something built in like remote => true for the form itself? Can someone point me in the right direction?
EDIT:
I found this SO question but I cannot figure out how to implement the answer.
Currently, my form looks like this;
= simple_form_for(#account)
= f.input :account_number
= f.input :area, collection: #areas
= f.submit nil, :class => 'btn btn-primary'
Based on the answer in the linked question, I should add something like this, but of course it is not working
= simple_form_for(#account)
= f.input :account_number
= f.input :area, collection: #areas, :input_html => {"data-remote" => true, "data-url" => "/my_areas", "data-type" => :json}
= f.submit nil, :class => 'btn btn-primary'
I can think of two ways to go about this if you don't want to load the contents initially when the page loads. One way is to run a script after the DOM has loaded to change the options for the select tag and the other is to collect the options when you click on the drop-down on the select element. I might go for the first way because there wouldn't be latency when a user clicks on the select element--they wouldn't have to wait for the options to populate.
So you'd run a jQuery script on document ready that makes an AJAX call to a method in your controller, which then returns the collections you want, then you iterate through the select elements you want to change with JQuery scripts. It might look something like this.
# in view with the select options to be changed
$(document).ready(function() {
$.get(change_selects_path, function(response) {
$.each(response, function(args) {
// code for each select element to be changed
$('.class_of_select_element').html(<%= j options_from_collection_for_select(args) %>);
});
});
)};
# in controller
def change_selects
# make db calls and store in variables to feed to $.get request
end
Note that this not tested but should give you a good start towards a solution. For further info on the each loop, you can check out this documentation.
Not sure if this fits your exact use case (please clarify if not), but I also have a few collection selects that have a large amount of database rows behind them. I use the select2-rails gem to take care of this. Users can begin to type in the name and the relevant results will show up (it will also show a few initially if they don't type something).
Check it out here: https://github.com/argerim/select2-rails
Edit: For a cascading dropdown, I recommend this gem: https://github.com/ryanb/nested_form

pre-populating form field from database with "second level" association

I have three models: Appointment, Client, and InsuranceProvider
A client has_many :appointments
And a client has_many :insurance_providers (the idea being I"d like to store historical info there).
in my view to create a new appointment, I have this (among other things):
<%= f.association :client, label_method: lambda { |c| "#{c.first_name} #{c.last_name}" }, collection: current_user.clients %>
this is fine, but I'd like to get to the copay field in insurance_providers.
Basically, this is how you'd get there:
appointment.client.insurance_provider.copay
What I'd like to do is pre-populate the "copay amount" field based on the client selected from the dropdown.
How can I do this?
Please let me know if you need to see my models or views explicitly.
If I understand correctly, you want a second select to be populated with values based on the value in the association.
Basically, you need JQuery/AJAX to do this for you. JQuery to watch the first select, and then AJAX to get data from rails based on the value chosen, and JQuery again to add values to the second select.
An alternative would be to use an in-place editor like best_in_place for each select, which would do the AJAX-y stuff for you.
Use ajax to to fetch the values for copay based on the return of the select.
Because there are a lot of steps, I'll lay them out, but you can find them in probably a dozen other SO questions.
Add the Javascript, this coffeescript but it's just your basic on change -> send-data call - so change at will.
#appointment.js.coffee
$(document).ready ->
$(".client_select").on "change", ->
$.ajax
url: "/appointments/new"
type: "GET"
dataType: "script"
data:
client: $(".client_select").val()
Make sure your form has the 2 jquery elements to get data from and push data to.
# First the field to pull from
<%= f.association :client, label_method: lambda { |c| "#{c.first_name} #{c.last_name}" }, collection: current_user.clients, input_html: { class: 'client_select' } %>
# And then the field to push to
<%= f.input :copay_amount, input_html: { class: 'copay_from_client' } %>
This is going to make a request on your "new" action of your appointments controller, so you'll need to add a javascript respond to to make sure it can render the next step, the UJS file.
# appointments_controller.rb
def new
# ... All the stuff you're normally doing and additionally:
#you'll have to adjust the params argument to match your select field
insurance_copay = Client.find(params[:client]).insurance_provider.copay
respond_to do |format|
format.html # new.html.erb
format.js { render "new", locals:{insurance_copay: insurance_copay} }
format.json { render json: #appointment }
end
end
Now add the UJS, new.js.erb
$(".copay_from_client").val('<%= #insurance_copay %>');

Array as Parameter from Rails Select Helper

I'm working on a legacy project that is using acts_as_taggable_on which expects tags to come in arrays. I have a select box allowing users to select a tag on a Course in a field called categories. The only way mass assignment create will work is if params looks like this params = {:course => {:categories => ['Presentation']}}. I've currently a view with this helper:
<%= f.select 'categories', ['Presentation' , 'Round Table' , 'Demo', 'Hands-on'] %>
Which will give me a parameter like params = {:course => {:categories => 'Presentation'}}. This doesn't work since Acts as tag gable apparently can't handle being passed anything other than a collection.
I've tried changing categories to categories[] but then I get this error:
undefined method `categories[]' for #<Course:0x007f9d95c5b810>
Does anyone know the correct way to format my select tag to return an array to the controller? I'm using Rails 3.2.3
I didn't work with acts_as_taggable_on, but maybe this simple hack will be suitable for you? You should put it before mass-assignment.
category = params[:course][:categories]
params[:course][:categories] = [category]
If you are only going to allow the selection of ONE tag, you could do:
<%= f.select 'categories', [['Presentation'] , ['Round Table'] , ['Demo'], ['Hands-on']] %>
Each one item array will have first for the display value, and last for the return value, which in this case will both return the same thing, as the first element of the array is the same as the last element when the array as one element.
Seems like select doesn't give you that option.
If I understand correctly, one option might be to use a select_tag instead and just be explicit about where you want the selection in the params:
<%= select_tag 'course[categories][]', options_for_select(['Presentation' , 'Round Table' , 'Demo', 'Hands-on']) %>
That ought to get your params the way you need them.
Here's what I'm using for one of my projects:
<% options = { include_blank: true } %>
<% html_options = { required: true, name: "#{f.object_name}[#{resource.id}][days][]" } %>
<%= f.select :days, DAYS, options, html_options %>
Without html_options[:name], Rails handles the name of the select tag and spits out something like
service[service_add_ons_attributes][11][days]
but I need
service[service_add_ons_attributes][11][days][]
So I override it.
Hope that helps.

Resources