Rails - Merge Multi-Select Params into comma-separated string - ruby-on-rails

I have a select box that allows multiple values, to filter the results on the page. When I select multiple, the Parameters that are submitted look like this:
Parameters: {"categories"=>["books", "films"], "commit"=>"Submit", "id"=>"87"}
When I am returned to the page, the URL is:
http://localhost:3000/87/projects?categories%5B%5D=books&categories%5B%5D=films&commit=Submit
The URL I would like to return is:
http://localhost:3000/87/projects?categories=books,films
How can I return these params[:categories] as a comma-separated string in the URL? Also, is it possible to remove the "&commit=Submit" from the URL?
Here is my full form code:
<%= form_with url: project_path(#project), local: true, method: :get, skip_enforcing_utf8: true do |form| %>
<%= form.select(:categories, #categories.map {|category| [category.name,category.slug]}, options = { selected: params[:categories], include_blank: "Select Categories", include_hidden: false }, html_options = { multiple: true }) %>
<%= form.submit 'Submit' %>

There's a couple JS & Rails way to do what you want. I can think of a quick and easy one using rails only: Redirecting the URL you are getting to another route with the data parsed as you want it. Like this -->
Assuming this is your route to project_path : get 'project', to: 'project#reroute', as: :project
You can go to your reroute method in the project controller and parse the data you got.
project_controller.rb
class ProjectController < ApplicationController
def reroute
redirect_to your_path(categories: params[:categories].join(','))
end
end
This converts your categories array to a string with your values separated by commas. It is not an array anymore. and it also removes "&commit=Submit" like you wanted.
If you dislike the rails routing method, you can also make your submit button to run some JS functions that builds the url string as you want it. For example <%= submit_tag , :onclick => "return buildUrl();" %>
Having this said, I must say I agree with Edward's comment, the url encoded format is standard and works out of the box, no need for all the additional rerouting and parsing. Im pretty sure whatever you need the data for can be used with the URL encoded format with proper parsing.

Related

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 %>');

Rails form using GET request: How to remove button and utf8 params?

I'm just trying to create a simple select menu that takes you to a specific URL. So far I have something like this:
# haml
= form_tag new_something_path, method: :get do
= select_tag :type, options_for_select(my_array)
= submit_tag 'New Something'
However, when I submit the form I get the UTF8 parameter as well as a "commit" parameter with the text of the button.
How can I remove the UTF8 and commit parameters?
Removing the commit param is relatively simple, you need to specify that the input does not have a name:
submit_tag 'New Something', name: nil
Regarding the UTF-8 param...it serves an important purpose. Once you understand the purpose of the Rails UTF-8 param, and for some reason you still need to remove it, the solution is easier than you think...just don't use the form_tag helper:
# haml
%form{action: new_something_path, method: 'get'}
= select_tag :type, options_for_select(my_array)
= submit_tag 'New Something', name: nil
You can get rid of the utf8 param by adding the enforce_utf8: false option of form_tag (and also form_form) like the following:
= form_tag new_something_path, method: :get, enforce_utf8: false do
(thanks to #Dmitry for pointing that out)
But please make sure you don't need it: What is the _snowman param in Ruby on Rails 3 forms for? (I'm not sure if it is actually relevant for GET forms.)
The additional parameter is generated by the submit button can be removed by setting the name: false option on your submit_tag (Also works for submit in case of form_for).
= submit_tag 'New Something', name: nil

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.

How do I build link with specific part of params hash from previous request?

While I wouldn't normally create a page like this, please know this is a current constraint I can't change.
The page has a checkbox form as well as a table with links for THs that sort the table. I need to construct the TH link in a way that it retains the checkbox items already checked.
Checkbox constructed in View with Haml as:
= form_tag movies_path, :method => :get do
Include:
- #all_ratings.each do |rating|
= rating
= check_box_tag "ratings[#{rating}]", "1", (#ratingsarray.include?(rating) ? true : false)
= hidden_field_tag 'sort', #sort
= submit_tag 'Refresh'
Then for the table it has this for the TH
%th{:class => #classrelease_date}
%a#release_date_header= link_to "Release Date", movies_path(:sort=>'release_date', :params[:ratings]=>params[:ratings])
Ultimately I want the URL like "/moves/?sort=release_date&Ratings[PG]=1&Ratings[G]=1" where I am spitting out the ratings params back to the page as part of the URL. Or how to I pass the ratings params in any part of page where the existing controller code will read it.
Existing controller code access ratings from checkbox:
params[:ratings]
Since movies_path accepts hash as parameter, you can tailor params and then generate the URL with movies_path(params). Generally, you may need to remove "controller" and "action" from params.

How to display Rails select field values rather than stored integers in other views

I'm using a select field in a Rails app that is NOT tied to a related model, but stores integer values for a static series of options , i.e.,
<%= select (:this_model, :this_field, [['Option1',1],['Option2',2],['Option3',3],['Option4',4]] ) %>
In a show/ index view, if I want to display the option text (i.e. Option1, Option2, etc) rather than the integer value stored in the database, how do I achieve this?
Thanks for helping a noob learn the ropes!
EDIT
Based on Thorsten's suggestion below, I implemented the following. But it is returning nil, and I can't figure out why.
Invoice model:
##payment_status_data = { 1 => "Pending Invoice" , 2 => "Invoiced" , 3 => "Deposit Received", 4 => "Paid in Full"}
def text_for_payment_status
##payment_status_data[payment_status]
end
Invoice show view:
Payment Status: <%= #invoice.text_for_payment_status %>
In the console:
irb > i=Invoice.find(4)
=> [#<Invoice id: 4, payment_status: 1 >]
irb > i.text_for_payment_status
=> nil
I've tried defining the hash with and without quotes around the keys. What am I missing?
something like this would work:
<%= form_for #my_model_object do |form| %>
<%= form.label :column_name "Some Description" %>
<%= form.select :field_that_stores_id, options_for_select({"text1" => "key1", "text 2" => "key2"}) %>
<% end %>
Update
If you later want to display the text you can get it from a simple hash like this:
{"key1" => "text 1", "key2" => "text2"}[#my_object.field_that_stores_id]
But you better store this hash somewhere in a central place like the model.
class MyModel < ActiveRecord
##my_select_something_data = {"key1" => "text 1", "key2" => "text2"}
def text_for_something_selectable
##my_select_something_data[field_that_stores_id]
end
end
Then you can use it in your views like
#my_object.text_for_something_selectable
There are many possible variations of this. But this should work and you would have all information in a central place.
Update
Ok, I used something similar for our website. We need to store return_headers for rma. Those need to store a return reason as a code. Those codes are defined in an external MS SQL Server Database (with which the website exchanges lots of data, like orders, products, and much more). In the external db table are much more return reasons stored than I actually need, so I just took out a few of them. Still must make sure, the codes are correct.
So here goes he model:
class ReturnHeader < AciveRecord::Base
##return_reason_keys = {"010" => "Wrong Produc",
"DAM" => "Damaged",
"AMT" => "Wrong Amount"}
def self.return_reason_select
##return_reason_keys.invert
end
def return_reason
##return_reason_keys[nav_return_reason_code]
end
end
Model contains more code of course, but that's the part that matters. Relevant here is, that keys in the hash are strings, not symbols.
In the views i use it like this:
In the form for edit:
<%= form_for #return_header do |form| %>
<%= form.label :nav_return_reason_code "Return Reason" %>
<%= form.select :nav_return_reason_code, options_for_select(ReturnHeader.return_reason_select, #return_header.nav_return_reason_code) %>
<% end %>
(Maybe no the most elegant way to do it, but works. Don't know, why options_for_select expects a hash to be "text" => "key", but that's the reason, why above class level method returns the hash inverted.)
In my index action the return reason is listed in one of the columns. There I can get the value simply by
#return_headers.each do |rh|
rh.return_reason
end
If you have trouble to get it run, check that keys a correct type and value. Maybe add some debug info with logger.info in the methods to see what actual data is used there.

Resources