How to default collection_check_boxes to checked? - ruby-on-rails

I have this line that I'm trying to default to checked <%= f.collection_check_boxes :committed, checked, Date::ABBR_DAYNAMES, :downcase, :to_s, %>
In db t.text "committed".
I tried variations of checked & true, but maybe I overlooked something.
Here's the Gist of it.

Here is a quick answer on how to add checked as the default to the collection_check_boxes form helper since it took me some time to figure it out. Break it into a block and you can set checked and add classes. More info at http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-collection_check_boxes.
<%= f.collection_check_boxes(:author_ids, Author.all, :id, :name) do |b| %>
<%= b.label(class: "check_box") { b.check_box(checked: true, class: "add_margin") + b.text } %>
<% end %>

You are using a form_for, so that f is a form builder. This means that it is bound to the object you initialized it with, let's call it #habit. Since you're calling collection_check_boxes on the form builder, it will do something like #habit.send(:commit) to consult whether or not it should have the check box checked, and currently (apparently) it is not. In other words, if you want to use form_for you need to have this "everything is checked" fact represented in the model itself.
Now I am not sure what your model layer looks like, so I'll address a few scenarios. If you have a has_and_belongs_to_many relationship like this:
class Habit < ActiveRecord::Base
has_and_belongs_to_many :committed_days
end
class CommittedDay < ActiveRecord::Base
has_and_belongs_to_many :habits
# let's assume it has the columns :id and :name
# also, let's assume the n:m table committed_days_habits exists
end
Then I think the easiest way is in the controller itself do something like this:
def new
#habit = Habit.new
#habit.committed_day_ids = CommittedDay.all.map(&:id)
end
And in your ERB do:
<%= f.collection_check_boxes(:committed_day_ids, CommittedDay.all, :id, :name)
Now, it might be an overkill to do this with a has-and-belongs-to-many, especially with days of week (it means the CommittedDay table has 7 records, one for each day, which is kinda awkward). So you could also consider simply serializing an array of days of week into the db, and then just make sure the default for that column contains all of them.
The ERB will be similar to what you wrote:
<%= f.collection_check_boxes :committed, Date::ABBR_DAYNAMES, :downcase, :to_s %>
If you're using Postgres your class can be simply:
class Habit < ActiveRecord::Base
end
And the serialization code would be in the migration:
# downcase is used since in the ERB you are using :downcase for the id method
t.text :committed, default: Date::ABBR_DAYNAMES.map(&:downcase), array: true
If you are not using Postgres, you can use Rails serialization which is DB agnostic:
class Habit < ActiveRecord::Base
serialize :committed, Array
end
And then your migration would look like this:
t.text :committed, default: Date::ABBR_DAYNAMES.map(&:downcase).to_yaml

Related

Ruby on Rails Form for Calculating Dice Rolls

I am creating a web application as a training project that will take three inputs (Integer values for number of dice, number of sides and number of rolls) and input that into a series of tables that will facilitate calculations and the output of the results, and the saving of historical data (past rolls.)
I'm stuck.
Here is my ERB for the form:
<%= form_for(#run) do |f| %>
How many dice: <%= f.number_field :die_count, :placeholder => "# of dice" %><br>
How many sides: <%= f.number_field :die_sides, :placeholder => "# of sides" %><br>
How many rolls: <%= f.number_field :rolls, :placeholder => "# of rolls" %><br>
<%= f.submit "Roll!" %>
<% end %>
Here's my Model:
class Run < ActiveRecord::Base
has_many :rolls
belongs_to :user
validates_presence_of :die_count, :rolls, :die_sides
validates_numericality_of :die_count, :die_sides, :rolls
end
Here's my pertinent Controller info:
def new
#run = Run.new
end
def create
#run = Run.new(run_params)
end
private
def run_params
params.require(:run).permit(:die_count, :die_sides, :rolls)
end
For good measure, here's the migrate for making my DB table:
class CreateRuns < ActiveRecord::Migration
def change
create_table :runs do |t|
t.integer :die_count
t.integer :die_sides
t.integer :roll_count
t.timestamps null: false
end
end
end
At this point, I just want it to create a new record in the Run model. Here's the error I'm getting with the input of 5, 5, and 5 into the number_fields and click my submit button:
If I am reading this correctly, my application wants to iterate through the individual numbers as if they were arrays, but it can't because they are strings. This confuses me because they aren't strings in the first place. They are integers.
Pulling my hair out on this one.
:rolls is a reference to another model called Roll (have you created it?).
:roll_count is the attribute of your Run model that defines the number of rolls (as visible in your migration table).
They are two very different things, even though it stands to reason that :roll_count should in theory be equal to the number of Roll records your Run record 'owns'.
In your form, replace :rolls by :roll_count. Do the same in the run_params method in your controller. And also in the two validations in your model. (checklist: that's 4 places where you need to modify that symbol name).
In your controller, also change the content of your create method to #run = Run.create(run_params).
Edited (clarity): Run.new(params) creates a new instance of a Run object but doesn't save it in your DB. Run.create(params) creates the object AND saves it.
Finally, in your model, comment out the has_many :rolls until you have actually implemented the Roll model (no pun intended!) ; do the same for belongs_to :user if the User model is not implemented either.
After these changes, your form should normally work and data should be saved in your runs database table.
I believe you meant to replace rolls with roll_count everywhere in your code.
How many rolls: <%= f.number_field :roll_count, :placeholder => "# of rolls" %><br>
and
validates_presence_of :die_count, :roll_count, :die_sides
validates_numericality_of :die_count, :die_sides, :roll_count
and
def run_params
params.require(:run).permit(:die_count, :die_sides, :roll_count)
end

How to use collection_select in rails 4 from a model module?

I am trying to use collection_select tag for the default _form.html.erb using a concern/module, I need to set a hash including some department names.
Here is my app/models/concerns/SetDepartment.rb
module Set_Department
extend ActiveSupport :: Concern
def department
department {
1=>"Amatitlán",
2=>"Chinautla",
3=>"Chuarrancho"
}
end
end
Here is the model where I want to call the department method:
class Aplicante < ActiveRecord::Base
include SetDepartment
validates :titulo_id, :primer_nombre,
:primer_apellido, :dpi, :direccion_linea_1,:zona, :department_id, :username,
presence: true
validates :dpi,:username, uniqueness: true
has_secure_password
end
Now, I need to include this hash in a collection_select tag on my app/views/applicants/_form.html.erb
#...
<div class="field">
<%= f.label :department_id %><br>
<%= f.collection_select :department_id, Aplicante.department, Aplicante.department %>
</div>
#...
Obviously, this does not work but I can not think on anything else.
I have searched through the internet but I just get tough explinations and none of them involves a module... is it even possible?
Solved!
I was using the wrong method..
We can not use a collection_select helper with a hash, instead, we need to use the regular select method.
Collection_select is used when you have two models and you want to combine their different values in a drop down menu.
Information about how to use the select tag with a hash here:
http://apidock.com/rails/ActionView/Helpers/FormTagHelper/select_tag

select_check_boxes with an has many through relation and :checked option

I am using collection_check_boxes to create object in a has_many through relation;
here some models:
#emotion.rb
class Emotion < ActiveRecord::Base
has_many :emotional_states
has_many :reports, :through => :emotional_states
end
#report.rb
class Report < ActiveRecord::Base
has_many :emotional_states
has_many :emotions, :through => :emotional_states
[... other "irrelevant" stuff here ...]
end
#emotional_states.rb
class EmotionalState < ActiveRecord::Base
belongs_to :report
belongs_to :emotion
end
As you may understand when I create a Report I also select with a collection_check_box a list of Emotions I want to bind to that report (through the model EmotionalState); Everything works on create (I retrieve the hash values and if #report.save I also create EmotionalStates with the #report.id and #emotion.id.)
But when it cames to edit the Report I would like to edit also the associated EmotionalStates (this means creating new EmotionalStates or deleting old one).
How can I populate the select_check_boxes with ALL the available Emotions having checked that emotions that are alredy associated through the EmotionalStates bojects?
If I write something like:
<%= collection_check_boxes(:report, :emotion_id, #report.emotional_states.map{|e| e.emotion}, :id, :name) %>
I'll get a unchecked checkbox for every alredy associated Emotion.
<%= collection_check_boxes(:report, :emotion_id, Emotion.all, :id, :name, :checked => #report.emotional_states.map{|e| e.emotion}) %>
While this code will correctly returns Emotion.all, but will not check the emotions alredy associated to #report through #report.emotional_states.
I've searched all around the wheb for examples on the usage of :checked options for collection_select_boxes without any results...
any hint?
I did the same once in this way.you can also try :
Emotions:
<% Emotion.all.each do |emotion| %>
<%= check_box_tag 'report[emotion_ids][]' , emotion.id, #report.emotion.include?(emotion) %><%= emotion.name %><br/>
<% end %>
In Controller add :emotion_ids=>[] into strong parameters.
And one line into controller update method:
params[:report][:emotion_ids] ||= []
After coming back to this bug I discovered that the problem was an incorrect use of the .map method, mapping a whole object (e.emotion) instead its id (e.emotion.id).
This easily fixed my problem:
<%= collection_check_boxes(:report, :emotion_id, Emotion.all, :id, :name, :checked => #report.emotional_states.map{|e| e.emotion.id}) %>
Thank you for your help!

How to have a drop down <select> field in a rails form?

I am creating a scaffold -
rails g scaffold Contact email:string email_provider:string
but I want the email provider to be a drop down (with gmail/yahoo/msn as options) and not a text field. How can I do this ?
You can take a look at the Rails documentation . Anyways , in your form :
<%= f.collection_select :provider_id, Provider.order(:name),:id,:name, include_blank: true %>
As you can guess , you should predefine email-providers in another model -Provider , to have where to select them from .
Or for custom options
<%= f.select :desired_attribute, ['option1', 'option2']%>
You create the collection in the Contact controller -
app/controllers/contacts_controller.erb
Adding
#providers = Provider.all.by_name
to the new, create and edit methods, using a scope for the by_name in the Provider model - app/models/provider.rb - for the ordering by name
scope by_name order(:name)
Then in the view - app/views/contacts/_form.html.erb - you use
<%= f.collection_select :provider_id, #providers, :id, :name, include_blank: true %>
For rails forms, I also strongly recommend you look at a form builder like simple_form - https://github.com/plataformatec/simple_form - which will do all the heavy lifting.
This is a long way round, but if you have not yet implemented then you can originally create your models this way. The method below describes altering an existing database.
1) Create a new model for the email providers:
$ rails g model provider name
2) This will create your model with a name string and timestamps. It also creates the migration which we need to add to the schema with:
$ rake db:migrate
3) Add a migration to add the providers ID into the Contact:
$ rails g migration AddProviderRefToContacts provider:references
4) Go over the migration file to check it look OK, and migrate that too:
$ rake db:migrate
5) Okay, now we have a provider_id, we no longer need the original email_provider string:
$ rails g migration RemoveEmailProviderFromContacts
6) Inside the migration file, add the change which will look something like:
class RemoveEmailProviderFromContacts < ActiveRecord::Migration
def change
remove_column :contacts, :email_provider
end
end
7) Once that is done, migrate the change:
$ rake db:migrate
8) Let's take this moment to update our models:
Contact: belongs_to :provider
Provider: has_many :contacts
9) Then, we set up the drop down logic in the _form.html.erb partial in the views:
<div class="field">
<%= f.label :provider %><br>
<%= f.collection_select :provider_id, Provider.all, :id, :name %>
</div>
10) Finally, we need to add the provders themselves. One way top do that would be to use the seed file:
Provider.destroy_all
gmail = Provider.create!(name: "gmail")
yahoo = Provider.create!(name: "yahoo")
msn = Provider.create!(name: "msn")
$ rake db:seed
<%= f.select :email_provider, ["gmail","yahoo","msn"]%>
Please have a look here
Either you can use rails tag Or use plain HTML tags
Rails tag
<%= select("Contact", "email_provider", Contact::PROVIDERS, {:include_blank => true}) %>
*above line of code would become HTML code(HTML Tag), find it below *
HTML tag
<select name="Contact[email_provider]">
<option></option>
<option>yahoo</option>
<option>gmail</option>
<option>msn</option>
</select>
Rails drop down using has_many association for article and category:
has_many :articles
belongs_to :category
<%= form.select :category_id,Category.all.pluck(:name,:id),{prompt:'select'},{class: "form-control"}%>
In your model,
class Contact
self.email_providers = %w[Gmail Yahoo MSN]
validates :email_provider, :inclusion => email_providers
end
In your form,
<%= f.select :email_provider,
options_for_select(Contact.email_providers, #contact.email_provider) %>
the second arg of the options_for_select will have any current email_provider selected.
I wanted to display one thing (human readable) but store another (an integer id).
Small example
Here's a small example that helped:
<%= form.select(:attribute_name, {cat: 5, dog: 3} )%>
The {cat: 5, dog: 3} will display "cat" and "dog", but save 5 and 3.
Real world example
Here's the actual use case. It displays the names of sellers (that humans can read), but saves the sellers' id (an integer):
<div class="field">
<%= form.label :seller_id %>
<%= form.select :seller_id, seller_names_and_ids(), {include_blank: true}, {required: true, class: "form-control"} %>
</div>
And the helper is defined as:
def seller_names_and_ids
# We want this to produce a hash of keys (the thing to display) and values (the thing to save,
# in thise case the seller_id integer)
sellers = Seller.all
h = {}
sellers.each do |seller|
thing_to_display = seller.name + " (" + seller.id.to_s + ")"
thing_to_save_in_db = seller.id
h.store(thing_to_display, thing_to_save_in_db)
end
h
end

Validating field's presence fails even though the field is not blank

I'm trying to fill out an array with values from checkboxes. It works just fine when creating a record, but fails validation when editing. The params look right, which is what really confuses me:
"record"=>{... "type_array"=>["accounting"], ...}
It looks the same as the params from creating a new record. The fields in New.html.erb and Edit.html.erb also use the same markup.
Edit.html.erb
<div class="field">
<%= f.label :type_array, "What type of record?" %><br />
<% ["accounting", "agriculture", "automotive"].each do |type| %>
<%= check_box_tag 'record[type_array][]', type, (true if #record.type_list.include? type),
:id => type %>
<%= label_tag type, type.titleize, :class => type %><br />
<% end %>
</div>
Parts of Record.rb
validates :type_array, :presence => true
attr_accessor :type_array
attr_accessible :type_array
before_validation :set_type_list
private
def set_type_list
self.type_list = type_array.join ',' if type_array.present?
end
Am I missing something? When I remove the type_array validation and fill out the form, it acts like type_array is empty. Somewhere along the line, it must be lost or something.
I appreciate any help.
(Sidenote: if anyone has a better way to do the list of checkboxes, let me know)
Delete the line attr_accessor :type_array.
This creates accessor methods to a new instance variable, not to the model attribute type_array, which means that #record.type_array now refers to that instance variable instead of the attribute.
You almost never use attr_accessor or it's siblings attr_reader and attr_writer in Rails because you want to deal with model attributes, not instance variables.
Edit: You're using type_array as a virtual attribute.
class Record < ActiveRecord::Base
validates :type_array, :presence => true
attr_accessible :type_array
def type_array=(val)
self.type_list = val.join ','
end
def type_array
self.type_list.split ','
end
def type_array_before_type_cast
type_array
end
end
For the reason why you need that last function definition, see this question.

Resources