Custom Formtastic Control with Multiple Parameters - ruby-on-rails

I'm attempting to build a custom control for Formtastic that takes a latitude and a longitude, however, I'm not sure how to go about passing the method names through. Ideally I'd have the following in the semantic_form_for block:
f.input :latitude, :longitude, :as => :location
I've also tried passing with an array:
f.input [:latitude, :longitude], :as => :location
But in both cases, this fails - the first on the number of parameters, the second on the first parameter not being a symbol.
Is there any way of passing two methods into #input that I'm missing?

OK, I've sorted this out by writing a plugin for Formtastic.
I've added a multi_input function that can take any number of parameters and an (optional) options hash. I've also added a map_input type that outputs the map control and JS (framework agnostic).
More details at the above link.

You can't pass multiple methods to a single input. What you could do is have an accessor on your model called, uh, lat_long or similar, which could return a string or array or whatever in a format which your location_input recognises and knows how handle.

Related

Getting Globalize and autocomplete to work together

Using globalize gem to manage translations with autocomplete, there is a situation where a number of hooks need to be properly set. Note: this does not use hstore AFAIK. I have not managed to find a way to do so. The most productive set-up to date has
controller:
autocomplete :nation, :name, :full => true
Nation
translates :name
view
<%= autocomplete_field_tag 'nation_name', '', autocomplete_nation_name_itins_path, size: 35, :id_element => 'nation_id' %>
There is no inherent reference to nation_translations database table created by Globalize as of yet. As this image suggests, there is a problem:
Issue 1: The input remains binded to the base table's attribute value (I have not yet cleared them out as the Globalize gem suggests. Otherwise I'd be getting blanks). can is actually ready all values of canin master table... Typing in other locales, like cyrillic say Канада has naturally no effect as that value is not part of the Nation table.
What is interesting is that the drop-down values are being populated by Rails automatically, extracting the translation values of what is input.
Issue 2: I'd rather pass the parameter 'nation_id' which is naturally part of the nation_translations table with the form data. although I can append , :extra_data => [:nation_id] to the controller it is not being submitted (example in cyrillic where the input is given without any autocomplete)
{"utf8"=>"✓", "nation_name"=>"Канада", "commit"=>"..."}
Rails.logger.info :extra_data returns:
extra_data
Now the second issue can be overcome because a query like
Nation::Translation.where('name = ?', "Канада").pluck('nation_id')
returns a proper result. But that point is moot if the autocomplete is not playing ball with the user's input.
How can this be configured to have user input autocomplete with the current local translations?
this does get solved by creating an entirely new class with attributes nation_id, name, locale and can thus be called symbolically.
The query call is not that straightforward however. As the gem suggests, the method need to be tweaked
def autocomplete_nation_name
term = params[:term].downcase
locale = params[:locale]
nationtranslations = Nationtranslation.where('locale = ? AND name LIKE ?', locale, "%#{term}%").order(:name).all
render :json => nationtranslations.map { |nationtranslation| {:id => nationtranslation.id, :label => nationtranslation.name, :value => nationetranslation.name} }
end
intervening on the action method itself provides all the leeway desired...

Error `undefined method `state' for "AK":String` for `collection_select`

After adding the second method, uniq.pluck(:state) to the code below, I'm getting the following error message:
undefined method `state' for "AK":String.
I looked at all the posts on here and couldn't find anything related to this problem. Any insights or help you could offer would be greatly appreciated.
<%= f.collection_select :state, (Boli.order(:state).uniq.pluck(:state)), :id, :state, include_blank: true %>
Thank you #D-side, now having difficulties using grouped_collection. I'd like the user to be able to select a group of banks in a particular state. Getting the error message undefined method `map' for :id:Symbol, using the following code:
<%= f.grouped_collection_select :bank, :id, Boli.order(:bank), :id, :bank, include_blank: true%>
pluck with a single attribute name returns an array of attribute values. Strings, in your case.
collection_select, however, is built with model instances in mind, in that it accepts... well, the docs say it better anyway:
collection_select(method, collection, value_method, text_method, options = {}, html_options = {})
<...>
The :value_method and :text_method parameters are methods to be called on each member of collection. The return values are used as the value attribute and contents of each <option> tag, respectively. They can also be any object that responds to call, such as a proc, that will be called for each member of the collection to retrieve the value/text.
Obviously, since you've already fetched values of the attribute state, calling state on the resulting values once more is nonsensical.
You can fix this in multiple ways.
PostgreSQL's DISTINCT ON (expr)
By replacing .uniq.pluck(:state) with .select("DISTINCT ON (state) id, state") you'll get ActiveRecord model instances, so every element of the resulting collection will have methods id and state, as collection_select expects.
Or use the query you have, with pluck
...by giving procs instead of :id and :state that take a string as an argument and produce appropriate values.
It all boils down to what you need.

How do I properly use a select box with ruby on rails?

I have a select box for the event types of an event. event belongs to event_type and event_type has many events. Here's what I have for the select box:
<%= f.select :event_type, options_from_collection_for_select(EventType.all, :id, :name, #event.id), :placeholder => 'Select an event type' %>
But, the problem is that when I submit it, it submits the id of the event type and not the name of it. So, how would I make it submit the name rather than the id? If you need to see the any more code than just tell me, thanks
The second parameter to options_from_collection_for_select is the value that will be submitted with the form. You have :id, so change it to :name.
http://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/options_from_collection_for_select
(but this seems like a strange thing to do - typically you would store the event type ID.)
You can use the id after the submit to load the event type again in your controller post action like this:
selected_type = EventType.find(params[:event_type]
It is also a good practice to keep database calls to the controller, so please put the EventType.all statement in there and pass it as local or class variable like you did with event.
If you really want to pass the name in your form instead of the id, you can replace the :id part in your call to something more like this options_from_collection_for_select(#event_types, :name, :name, #event.event_type.name). Keep in mind that this value should be unique!
The method works like this:
options_from_collection_for_select(collection, value_method, text_method, selected = nil)
So the first parameter contains all the options, the second defines the value within those option objects which are put into the value field of the HTML option (which is being submitted by the form), the third defines the text which is displayed to the user and the final parameter defines the value of the selected entry (in case you are editing an entry for example). For the last parameter, you need to use the events' event_type id, or in your case, the name because you set the value of your HTML tag to it.
Use pages like ApiDock or the Rails tutorials to get examples for some of these methods.
http://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/options_from_collection_for_select
You should pass name in the Value method, if you want to pass the name,
<%= f.select :event_type, options_from_collection_for_select(EventType.all, :name, :name, #event.id), :placeholder => 'Select an event type' %>
Here is the doc for options_from_collection_for_select

rails bringing back all checkboxes instead of only selected one

I am using simple_form and have a following sample tag:
<%= f.input :medical_conditions, :label=>false, :collection => medical_conditons, :as => :check_boxes%>
The collection holds about 100 checkboxes. However, when user only selects 1 or 2, everything is still getting saved to the database like this:
---
- ""
- ""
- ""
medical_conditions is a simple array in my application_helper
def medical_conditons
t = [
"Allergies/Hay Fever",
"Diabetes",
"Heart Surgery"]
return t
end
the medical_conditions field is a :string field.
What do I need to do so that only values that are selected are saved in comma separated manner.
It is not simple_form behavior. It is from Rails. See this: http://d.pr/6O2S
Try something like this in your controller (guessing at how you wrote your create/update methods)...
params[:medical_conditions].delete('') #this will remove the empty strings
#instance.update_attribute(:medical_conditions, params[:medical_conditions].join(','))
#or however you want to save the input, but the key is the .join(',') which will
#create a comma-separated string of only the selected values, which is exactly
#what you're looking for me thinks :-)
If that does the trick for you, I'd consider making a private helper method that formats the params for you so that you can use it in #create, #update, or wherever else you need it. This should keep things a little cleaner and more "rails way-ish" in your crud actions.

String concatenation not possible?

So over the last 2 hours, I've been trying to fill a combobox with all my users. I managed to get all firstnames in a combobox, but I want their full name in the combobox. No problem you would think, just concatenate the names and you're done. + and << should be the concatenation operator to do this.So this is my code:
<%= collection_select(:user, :user_id, #users, :user_id, :user_firstname + :user_lastname, {:prompt => false}) %>
But it seems RoR doesn't accept this:
undefined method `+' for :user_firstname:Symbol
What am I doing wrong?
What you need to do is define a method on the User model that does this concatenation for you. Symbols can't be concatenated. So to your user model, add this function:
def name
"#{self.first_name} #{self.last_name}"
end
then change the code in the view to this:
<%= collection_select(:user, :user_id, #users, :user_id, :name, {:prompt => false}) %>
Should do the trick.
This isn't really rails giving you an error, it's ruby. You're trying to combine the symbols :user_firstname and :user_lastname
A symbol is a variable type, just like integer, string, or datetime (Well technically they're classes, but in this context we can think of them as variable types). They look similar to strings, and can function similarly to them, but there is no definition for the behavior of symbol concatenation. Essentially you're trying to send the method user_firstnameuser_lastname which is just as non-sensical as trying to concat two Symbols.
What you need to understand is that this parameter is looking for a method on your User object, and it won't understand the combination of two symbols. You need to define a method in your model:
def fullname
[user_firstname, user_lastname].reject{|v| v.blank?}.join(" ")
end
This'll return your first + last name for you, and then in the parameter you should send :fullname (because that's the method it'll call on each user object in the collection):
<%= collection_select(:user, :user_id, #users, :user_id, :fullname, {:prompt => false})%>
Also, it's considered poor practice to prefix every single column with the table name. user.user_firstname just looks redundant. I prefer to drop that prefix, but I guess it's mostly up to personal preference.
The arguments for value and display attribute are method names, not expressions on a user object.
To control the format more precisely, you can use the select tag helper instead:
select("user", "user_id", #users.each {|u| [ "#{u.first_name u.last_name}", u.user_id ] })
The docs are pretty useful.

Resources