Rails Select boolean with NULL value - ruby-on-rails

First the environment:
Rails 2.1.0, Ruby 1.8.7, Mysql 5.1.54, Ubuntu 11.04
I have a boolean field in my table which starts as NULL, but I can not find a good way to set it to NULL. (This field is basically a yes / no / unanswered field, which true / false / null seems about right for. The client specifically said he would like it to be able to remain null (unanswered).)
Here is my migration (specific names replaced):
class AddQuestionToClients < ActiveRecord::Migration
def self.up
add_column :clients, :question, :boolean
end
def self.down
remove_column :clients, :question
end
end
My controller uses a basic
#client = Client.find(params[:id])
#client.update_attributes(params[:client])
My view has a select (I think this is causing the problem, was never great with form helper selects):
<%= f.select :question, [["Yes", true], ["No", false]], { :include_blank => true, :selected => #client.question } %>
When selecting yes, true is passed; no, false is passed; blank, "" is passed.
But "" seems to automatically convert to false when updating the database.
To get around this, I'm doing this:
params[:client][:question] = (params[:client][:question] == "")? nil : params[:client][:question]
But this can't be the right way to do it. What am I missing?

I just ran into the same problem, and I solved it by making the attribute convert blank to null. In your case, I would add a method to your model class.
class Client < ActiveRecord::Base
def question=(value)
value = nil if value == ''
super(value)
end
end
This works for both create and update. (I'm using Rails 2.0.5.)

I think that you can do that only in that way server side, because when a data is posted is always not nil. So your solution is correct.
If you want to avoid that ugly code you can do a little trick client side using javascript. In fact if you use a disabled element instead of a blank value that value won't be posted and you get nil on server side.
To do that you can add a submit callback that checks whether the question field is blank and if so disable it before posting data. In that way it will work without any server side code.
To disable an element using jQuery you can see this link. So assuming your form has the #form id you can use this code
$(function() {
$('#form').submit(function () {
if ($('question-selector').val() == "") {
$('question-selector').attr('disabled', true);
}
})
});

I had the same problem, i needed null value by default for boolean to represent "not answered"
With Rails 3.2, when i pass "" for a boolean value (blank option) is sets the column to NULL in db.
So problem solved with newer version of rails :)

<%= f.select :question, options_for_select([["Yes", true], ["No", false]], :question) %>

#vdaubry I would only add that it is not really solved ..it is masked because you are using 'select' in your UI. If you are foolish enough to use checkboxes, like me (after all, they are booleans, right?) then your nice default nil value in the db, which we mean to represent 'unknown' is converted to 'false' when rendered in the view, and converted to false in the db, even when it is not updated in the view, same as was described above. In other words, it's a bit brittle to continue to rely on a 3rd value for 'boolean' being consistent.
Furthermore, if ever reporting on this attribute, your reporting code cannot easily deduce the values in the db, if "unknown" is a valid meaning and is obscured.
So I chose to refactor all my booleans making them strings ('yes', 'no', 'unknown') and use radio buttons in the views. Note this only matters for things like data collection UIs, where a user has not got round to finding out the truth, so statistically it matters a lot if it's "unknown".

This isn't mentioned in previous posts, but in Rails 5 this should work if you want to name the nil option
<%= f.select :question, [["Yes", true], ["No", false]], { :include_blank => 'Name this whatever you want, it will be nil when submitted', :selected => #client.question } %>
Tested this on my own project

Related

Rails/ActiveAdmin form: how to disable an input field based on the value of another (boolean) input field?

I have a model that has a boolean field and an array field that gets the values populated from another model (foreign key, not really relevant to my question).
In ActiveAdmin, I have a form like such:
form do |f|
f.semantic_errors
f.inputs do
f.input :boolean_field_name
f.input :array_field_name,
as: :searchable_select,
ajax: true,
input_html: { disabled: true }
end
f.actions
end
The disabled: true works, but I would like to replace the true with something that evaluates whether or not the input checkbox for boolean_field_name has been checked on the form (which by default it isn't).
I've tried params[:boolean_field_name], params.key?[:boolean_field_name], f.object.boolean_field_name, f.object[:boolean_field_name], resource[:boolean_field_name] and resource.boolean_field_name, but they all do nothing and evaluate to nil as far as I can tell.
I've even tried ModelName.find(params[:id]).boolean_field_name but of course since params[:id] is nil that doesn't work, and it wouldn't find a record with that id anyway because the record hasn't been created yet.
I've tried looking through the ActiveAdmin repository but I can't find the information I'm looking for in the source code either.
Is this even possible?
Did you mean like this?
form do |f|
f.semantic_errors
f.inputs do
f.input :boolean_field_name
f.input :array_field_name,
as: :searchable_select,
ajax: true,
input_html: { disabled: f.object.boolean_field_name # <= does not work }
end
f.actions
end
Since the information is a bit ambiguous to me, I would firstly like to know:
From the description:
The disabled: true works, but I would like to replace the true with
something that evaluates whether or not the input checkbox for
boolean_field_name has been checked on the form (which by default it
isn't).
Did you mean that you wanna change the disabled attribute depending on the other form field after the page loads? If that's true, then you have to do it with javascript as something like this:
let booleanField = document.querySelector('booleanField'),
arrayField = document.querySelector('arrayField');
booleanField.addEventListener('change', function(e) {
arrayField.setAttribute('disabled', booleanField.value);
})
Or if you mean you just wanna set the value of the disabled attribute to what boolean_field initially is on page loads, it will bring us more information if you can debug with the tools like debug, debugger or binding.pry. It will be helpful to checkout what f.object.boolean_field_name returns. According to the information you provides, I guess it could really be nil.

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...

Rails: Optional lookup fields

In my Rails app I am using collection_select to provide a list of valid choices for the user. I stored only the id from the looked-up table. I then make a call to the looked-up class in the show view to retrieve the actual value for the id.
in edit:
= f.collection_select :language_id, Language.find(:all, :conditions => ["supported = 't'"]), :id, :language, include_blank: false, :prompt => "Language"
in show:
%p
%b Language:
= Language.find(#article.language_id).language_code
This is all working fine unless no choice is made. If the #article.language_id field is null, the view cannot load as the find fails. Any suggestions on how to ignore null values and leave the field blank?
using find_by_id would be an option, as it does not raise an error if the id could not be found.
this issues a call to the database though.
so i would just check the languange_id and provide a default
= #article.language_id.nil? ? default : Language.find_by_id(#article.language_id)
this could also be moved into the model or a helper, so that it does not clutter your view code.
This is a common problem. Simply use try to leave nil values:
if #article.try(:language_id) != nil
#code = Language.find(#article.language_id).language_code
else
//default nil
#code = nil
end

form not saving to database in rails

I currently have a form:
<%= f.label(:price) %> <br/>
<%= f.text_field(:price, :value => number_to_currency(#object.price)) %>
I changed my migration from using float to using decimal:
change_column :object, :price, :decimal, :precision => 5, :scale => 2
On my view, I called it using:
<%= #object.price %>
For some reason, whenever I make a change to the form or in the console, it never saves the value and keeps it at $0.00 regardless of what i change it to. In the view, it always shows up as '0.0'. I am not sure what the problem is.
Do you have a table named object or objects or is this just an example?
If not, check your price column to make sure the migration worked properly.
If you do have a proper column type in your table, check to see if you're using attr_accessible in the Object class (and that price is included).
Also, if you are using Object as your class name, you may have some other issues here and I'd advise against it.
EDIT
number_to_currencymay prepend a $ in front of your cost, make sure you're entering your price without any currency before it or else I believe this would also result in 0.0 (can't parse decimal '$123.00', but can parse '123.00'

How can I format the value shown in a Rails edit field?

I would like to make editing form fields as user-friendly as possible. For example, for numeric values, I would like the field to be displayed with commas (like number_with_precision).
This is easy enough on the display side, but what about editing? Is there a good way to do this?
I am using the Rails FormBuilder. Upon investigation, I found that it uses InstanceTag, which gets the values for fields by using <attribute>_value_before_type_cast which means overriding <attribute> won't get called.
The best I have come up with so far is something like this:
<%= f.text_field :my_attribute, :value => number_with_precision(f.object.my_attribute) %>
Or my_attribute could return the formatted value, like this:
def my_attribute
ApplicationController.helpers.number_with_precision(read_attribute(:my_attribute))
end
But you still have to use :value
<%= f.text_field :my_attribute, :value => f.object.my_attribute %>
This seems like a lot of work.
I prefer your first answer, with the formatting being done in the view. However, if you want to perform the formatting in the model, you can use wrapper methods for the getter and setter, and avoid having to use the :value option entirely.
You'd end up with something like this.
def my_attribute_string
foo_formatter(myattribute)
end
def my_attribute_string=(s)
# Parse "s" or do whatever you need to with it, then set your real attribute.
end
<%= f.text_field :my_attribute_string %>
Railscasts covered this with a Time object in a text_field in episode #32. The really clever part of this is how they handle validation errors. It's worth watching the episode for that alone.
This is an old question, but in case anyone comes across this you could use the number_to_X helpers. They have all of the attributes you could ever want for displaying your edit value:
<%= f.text_field :my_number, :value => number_to_human(f.object.my_number, :separator => '', :unit => '', :delimiter => '', :precision => 0) %>
There are more attributes available too: http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html
If you want a format to be created or maintained during editing, you will need to add Javascript to implement "masks." Here is a demo.
It was the first hit in these results.
You can use the number_format plugin. By specifying a number_format for an existing numeric attribute inside your model, the attribute will now appear as formatted to Rails in all forms and views. It will also be parsed back from that format (when assigned via forms) prior to insertion into the database. (The plugin also creates purely numeric unformatted_<attribute-name> accessors which can continue to be used for arithmetic, or for direct numerical assignment or retrieval by you for seamless integration.)
class MyModel < ActiveRecord::Base
# this model has the balance attribute, which we
# want to display using formatting in views,
# although it is stored as a numeric in the database
number_format :balance,
:precision => 2,
:delimiter => ',',
:strip_trailing_zeros => false
def increment_balance
unformatted_balance += 10
end
You can also combine the above with a Javascript solution, which can force the user to maintain the decimal point and thousands separators in place while editing, although this is really not necessary.
I have done something similar. We format times and lengths using a custom form builder. It makes use of the existing text_field, but wraps it so the value can be customized:
class SuperFormBuilder < ActionView::Helpers::FormBuilder
include ApplicationHelper
include FormHelper
include ActionView::Helpers::TagHelper
include ActionView::Helpers::FormTagHelper
def length_field(label,*args)
scale = 'medium'
args.each do |v|
if v.has_key?(:scale)
scale = v[:scale]
v.delete(:scale)
end
end
value = length_conversion(#object.send(label.to_sym),scale)
options = (args.length > 0) ? args.pop : {}
return has_error(label, text_field_tag(field_name(label),value,*args) + ' ' + length_unit(scale))
end
private
def field_name(label)
return #object_name + "[#{label}]"
end
def has_error(label, output)
return "<div class='fieldWithErrors'>#{output}</div>" if #object.errors[label]
return output
end
And it is used like this:
<%= form_for( #section, {:action => 'save', :id => #section.id}, :builder => SuperFormBuilder) do |sf| %>
<%= sf.length_field :feed_size_min_w, :size => 3, :scale => 'small' %>
<% end %>
The end result is a value in the appropriate unit based off their choice on system (Metric, Imperial) and scale IE small = inches or millimeters.
I basically copied the text_field method from the existing form builder, which uses the text_field_tag itself.
There are two gotchas: 1) Knowing the name of the object field and how to access the object to get the value which you want to format. 2) Getting the name right so when the form is submitted it is part of the correct params hash.
The form builder is given a class variable #object. You can get the value of the field using the .send method. In my case I send the label :feed_size_min_w to the #object and get its length back. I then convert it to my desired format, and give it to the text_field_tag.
The name of the field is key to having it end up in the params hash, in my instance the params[:sections] one. I made a little helper function called field_name that takes care of this.
Finally the has_error wraps the field in an error div if there are errors on that label.
I needed "nicer" format on some specified text fields, resolved it by adding this to my initializers. Seems to work nicely on Rails ~= 5.2 and it should be easy to customize.
class ActionView::Helpers::Tags::TextField
private
def value_before_type_cast # override method in ActionView::Helpers::Tags::Base
v = super
# format as you like, when you like
if #options.delete(:nice_decimal)
v = v.to_s.gsub('.', ',') if v.is_a?(BigDecimal)
end
v
end
end
Usage in form f
<%= f.text_field :foo, nice_decimal: true %>

Resources