Display a checkbox list instead of multiple select - ruby-on-rails

I have a model MyModel with a serialized attribute a, describing an array of symbols.
This code works :
<% form_for #my_model do |f| %>
<%= f.select :a, MyModel::AS, :multiple => true) %>
<% end %>
The parameters are correct :
{ :my_model => { :a => [:a_value1, :a_value2] } }
I want to transform this multiple select into a set of checkboxes, like this :
<% form_for #my_model do |f| %>
<% MyModel::AS.each do |a_value|
<%= f.check_box(:a_value) %>
<% end %>
<% end %>
It works too, but the parameters are not the same at all :
{ :my_model => { :a_value1 => 1, :a_value2 => 1 } }
I think of 2 solutions to return to the first solution...
Transform my check_box into check_box_tag, replace multiple select, and add some javascript to 'check' select values when user clic on check_box_tags. Then, the parameter will be the same directly in the controller.
Add a litte code into the controller for 'adapting' my params.
What solution is the less ugly ? Or is there any other one ?

I found a solution, using 'multiple' option that I didn't know.
<% MyModel::AS.each do |a_value| %>
<%= f.check_box(:a, { :multiple => true }, a_value) %>
<% end %>
Result parameters are a little weird, but it should work.
{"my_model" => { "a" => ["0", "a_value1", "0", "a_value2", "0"] }
Edit from #Viren : passing nil at the end of the function like
<%= f.check_box(:a, { :multiple => true }, a_value, nil) %>
works perfectly.

There is another solution worth mentioning that makes it very easy to insert records into the database if you have a has_and_belongs_to_many or has_many through relationship by using the collection_check_boxes form helper. See documentation here.
<%= f.collection_check_boxes :mymodel_ids, MyModel::AS, :id, :name do |m| %>
<%= m.check_box %> <%= m.label %>
<% end %>
Then, in the controller we would allow the mymodel_ids attribute:
params.require(:mymodel).permit(:name, mymodel_ids:[])
We can also set up a model validation to require that at least one of the checkboxes be checked:
validates :mymodel_ids, presence: true
An added benefit of this method is that if you later edit the mymodel record and uncheck one of the checkboxes, its record will be deleted from the many_to_many association table on save.

You can do it like this:
<% MyModel::AS.each do |a_value| %>
<%= f.check_box("a[]", a_value) %>
<% end %>
This will make params come to server as follows
{ :my_model => { :a => [:a_value1, :a_value2] } }

if you already have a select_tag
<%= select_tag "filters", options_from_collection_for_select(filter_values, "id", "name", selected_ids), multiple:true, class:"form-control" %>
and wants to replace it with check_box_tag, you will need to implement something like this:
<div class="checkbox-inline">
<%= check_box_tag "filters[]", value.id, selected_ids.include?(value.id), { :multiple => true} %>
<%= value.name %>
</div>
notice the ending brackets on the name which is needed to catch check box results in the same parameter.
When I implemented this, the parameters were of the same format between the select_tag and the check_box_tag

Related

Rails select_tag to using scope to simple filter

I'm still learning Rails and in my project app admin should be able to filter all holiday leave requests by status (boolean field in db) using dropdown list (approved for true, pending for false). I think I have declared everything right in my controller and model however I have no idea how to implement this to the view. I was trying to follow up Austin's story blog and Using select_tag multiple => true to get a combined scope topic but still I don't figured how to do it.
leaves controller:
def index
#leave = Leave.new
#leaves = Leave.all.order(created_at: :desc).includes(:user)
#leaves = Leave.find_by('select = ?', params[:status])
end
model leave.rb
scope :approved, -> { where(status: true) }
scope :pending, -> { where(status: false) }
belongs_to :user, optional: true
view index.html.erb
<%= form_tag(action: :index) do %>
<div class="input-group">
<%= select_tag :status, options_for_select([['Approved', Leave.approved], ['Pending', Leave.pending]]) %>
<%= submit_tag 'Filter' %>
</div>
<% end %>
Change your code to this
def index
#leave = Leave.new # as you have added it, won't affect what we want to achieve.
#if params have status then only you've to show
#the selected ones otherwise you'll be showing all
#of them. right?
if params[:status].present?
#leaves = Leave.where(status: params[:status])
#dont use find_by it will return only one record instead of array.
else
#leaves = Leave.includes(:user).order(created_at: :desc)
end
end
<%= form_tag(url: admin_leaves_path, method: :get) do %>
<div class="input-group">
#Leave.approved will hit database from view. which is not recommended
#instead of that try using values which will be accessed in controller
#via params and then you can perform actions according to it.
<%= select_tag :status, options_for_select([['Approved', true], ['Pending', false]]) %>
<%= submit_tag 'Filter' %>
</div>
<% end %>

Rails check_box_tag within form_for

Is it possible to pass the value of checked check_box_tags within a form_for in Rails inside a hash?
Here is a very generic, basic version of the form:
<%= form_for(:object, :url => { :action => 'create', :id => params[:id]}) do |f| %>
<p>Field_a: <%= f.text_field(:field_a) %></p>
<p>Field_b: <%= f.text_field(:field_b) %></p>
<p>Field_c: <%= f.text_field(:field_c) %></p>
<p>Check boxes:</p>
<% check_box_choices_object_array.each do |s| %>
<%= check_box_tag(s.name, 1, false) %>
<%= .name %><br />
<% end %>
<%= submit_tag("Create") %>
<% end %>
Outputs roughly:
Field_a ___________________
Field_b ___________________
Field_c ___________________
Check boxes:
[] box_a
[] box_b
[] box_c
[] box_d
[] box_e
[] box_f
[] box_g
My problem is that since the available check boxes aren't actual fields in the object's table in the database (i.e. I'm not using check_box(:field) in the form), each checked check box gets passed as an individual parameter (i.e. "box_a" => "1", "box_b" => "1", "box_e" => "1"). I would like them to be passed as such:
:checked_boxes => {"box_a" => "1", "box_b" => "1", "box_e" => "1"}
This way, I can access them easily with params[:checked_boxes].
How do I do this, or, better yet, is there a better solution (I'm new to rails)?
I think you'd get the results you want if you wrap the checkboxes iterator in a fields_for :checked_boxes tag - or at least get you close to the results you want.
<%= form_for(:object, :url => { :action => 'create', :id => params[:id]}) do |f| %>
<p>Field_a: <%= f.text_field(:field_a) %></p>
<p>Field_b: <%= f.text_field(:field_b) %></p>
<p>Field_c: <%= f.text_field(:field_c) %></p>
<p>Check boxes:</p>
<%= f.fields_for :checked_boxes do |cb| %>
<% check_box_choices_object_array.each do |s| %>
<%= cb.check_box(s.name, 1, false) %>
<%= .name %><br />
<% end %>
<% end %>
<%= submit_tag("Create") %>
<% end %>
you can deal with no database attributes and models using attr_accessor
class Thing < ActiveRecord::Base
attr_accessible :name
attr_accessor :box_a, :box_b, :box_c
end
This way you can call these attributes in your form.

Drop-down menu in form with options from database with conditions? Rails 2.3.15

I'm writing a piece of an app that allows you to upload photos and assign them to a Bus. What I want is a drop-down menu with all the Name values. Buses can be either enabled or not. I'd like to restrict the list to just enabled buses. But I'm not sure how to do it in a form as a drop down menu.
This is all I know how to do:
<% Bus.find(:all, :conditions => {:is_enabled => 1).each do |bus| %>
<%= bus.name %>
<% end %>
But I don't know how to put this code in a form without it breaking in a variety of different ways, and I don't know any other way to get these names. This app uses an older version (Rails 2.3.15).
This was my best guess on how to do it:
<% form_for #bus_image, :html => { :multipart => true } do |f| -%>
<%= f.error_messages %>
<div class="field">
<%= f.label :name %><br />
<% Bus.find(:all, :conditions => {:is_enabled => 1}).each do |bus| %>
<%= select(bus.name) %>
<% end %>
<div>
<% end -%>
... which gives me wrong number of arguments (1 for 3).
Thanks, let me know if you need/want any more information.
Use this :
f.select(:bus_id, Bus.find(:all, :conditions => {:is_enabled => 1}).map{|b| [ b.name, b.id ] }, {:include_blank => true })
In place of :
<% Bus.find(:all, :conditions => {:is_enabled => 1}).each do |bus| %>
<%= select(bus.name) %>
<% end %>
Hope this will help.

Get an array from a Rails form

I need to design a form for a account resource. In that form, i need to collect some set of ids as an array in the params hash in attribute called relationships.
So the final params[account] hash from the POST request should be like:
{:name => 'somename', :relationships => ["123", "23", "23445"]}
How shall I design the form_for fields? I tried this, but didn't work:
<%= form_for #account do |f| %>
<%= f.text_field :name %>
<% #eligible_parents.each do |p| %>
<%= f.check_box "relationships", nil, :value => p.id %>
<b><%= p.name %></b><br/>
</span>
<% end %>
<%= f.submit "Submit" %>
<% end %>
Number of elements in #eligible_parents varies every time.
relationships is neither an association nor an attribute in account model.
I have to use virtual attributes but I need to fill in an array from a form.
Please help. How can I do this?
You still need a fields_for in your view, just use :relationships as the record_name then provide an object.
<%= form_for #account do |f| %>
<%= f.text_field :name %>
<% fields_for :relationships, #eligible_parents do |p| %>
<%= p.check_box "relationships", nil, :value => p.object.id %>
<b><%= p.object.name %></b><br/>
<% end %>
<%= f.submit "Submit" %>
<% end %>
Documentation here: ActionView::Helpers::FormHelper
I found this to be the cleanest way...
If you are working with straight data and want to send back an array without using any of these #objects:
<%= form_for :team do |t| %>
<%= t.fields_for 'people[]', [] do |p| %>
First Name: <%= p.text_field :first_name %>
Last Name: <%= p.text_field :last_name %>
<% end %>
<% end %>
your params data should return like this:
"team" => {
"people" => [
{"first_name" => "Michael", "last_name" => "Jordan"},
{"first_name" => "Steve", "last_name" => "Jobs"},
{"first_name" => "Barack", "last_name" => "Obama"}
]
}
If you want to send array of values just use [] in name attributes.In your case just use
<%= f.check_box "relationships", {}, :value => p.id, :name => "relationships[]" %>
In a complex form, with nested attributes, you can make use of the f.object_name helper. But beware of the syntax when doing interpolation. This is correct:
"#{f.object_name}[relationships][]"
This is NOT correct:
"#{f.object_name}[relationships[]]"
It always trips me up.
I'm working in something similar. My issue was how to pass the input name to a partial to resolve it as a hash using the rails form_for helper.
So, my solutions was something like this:
= render 'my_partial', form: form, name: "item_ids[item.id]"
So, this gonna render an html like this:
<input class="form-control" name="products[items_ids[1]]" id="products_items_ids[1]">
The number 1 is the item.id, this is how HTML understand that you gonna pass an array or hash, all depends your config.
So, the params gonna look like this:
"items_ids"=>{"1"=>"1"}
This is working for me with an form_object + virtus

Multiple forms for the same model in a single page

On the front page of my rap lyrics explanation site, there's a place where users can try explaining a challenging line:
alt text http://dl.dropbox.com/u/2792776/screenshots/2010-02-06_1620.png
Here's the partial I use to generate this:
<div class="stand_alone annotation" data-id="<%= annotation.id %>">
<%= song_link(annotation.song, :class => :title) %>
<span class="needs_exegesis"><%= annotation.referent.strip.gsub(/\n/, "\n <br />") %></span>
<% form_for Feedback.new(:annotation_id => annotation.id, :created_by_id => current_user.try(:id), :email_address => current_user.try(:email)), :url => feedback_index_path, :live_validations => true do |f| %>
<%= f.hidden_field :annotation_id %>
<%= f.hidden_field :created_by_id %>
<p style="margin-top: 1em">
<%= f.text_area :body, :rows => 4, :style => 'width:96%', :example_text => "Enter your explanation" %>
</p>
<p>
<% if current_user %>
<%= f.hidden_field :email_address %>
<% else %>
<%= f.text_field :email_address, :example_text => "Your email address" %>
<% end %>
<%= f.submit "Submit", :class => :button, :style => 'margin-left: .1em;' %>
</p>
<% end %>
</div>
However, putting more than one of these on a single page is problematic because Rails automatically gives each form an ID of new_feedback, and each field an ID like feedback_body (leading to name collisions)
Obviously I could add something like :id => '' to the form and all its fields, but this seems a tad repetitive. What's the best way to do this?
If you don't want to change your input names or your model structure, you can use the id option to make your form ID unique and the namespace option to make your input IDs unique:
<%= form_for Feedback.new(...),
id: "annotation_#{annotation.id}_feedback"
namespace: "annotation_#{annotation.id}" do |f| %>
That way our form ID is unique, i.e. annotation_2_feedback and this will also add a prefix, e.g. annotation_2_, to every input created through f.
Did you consider nested_attributes for rails models? Instead of having multiple new feedback forms where each is tied to an annotation, you could have multiple edit annotation forms where each annotation includes fields for a new feedback. The id's of the generated forms would include the annotations id such as edit_annotation_16.
The annotation model would have a relationship to its feedbacks and will also accept nested attributes for them.
class Annotation < ActiveRecord::Base
has_many :feedbacks
accepts_nested_attributes_for :feedbacks
end
class Feedback < ActiveRecord::Base
belongs_to :annotation
end
You could then add as many forms as you want, one for each annotation. For example, this is what I tried:
<% form_for #a do |form| %>
Lyrics: <br />
<%= form.text_field :lyrics %><br />
<% form.fields_for :feedbacks do |feedback| %>
Feedback: <br/>
<%= feedback.text_field :response %><br />
<% end %>
<%= form.submit "Submit" %>
<% end %>
<% form_for #b do |form| %>
Lyrics: <br />
<%= form.text_field :lyrics %><br />
<% form.fields_for :feedbacks do |feedback| %>
Feedback: <br/>
<%= feedback.text_field :response %><br />
<% end %>
<%= form.submit "Submit" %>
<% end %>
And the quick and dirty controller for the above edit view:
class AnnotationsController < ApplicationController
def edit
#a = Annotation.find(1)
#a.feedbacks.build
#b = Annotation.find(2)
#b.feedbacks.build
end
def update
#annotation = Annotation.find(params[:id])
#annotation.update_attributes(params[:annotation])
#annotation.save!
render :index
end
end
I had this same issue on a site I'm currently working on and went with the solution you mention at the bottom. It's not repetitive if you generate the ID programmatically and put the whole form in a partial. For example, on my site, I have multiple "entries" per page, each of which has two voting forms, one to vote up and one to vote down. The record ID for each entry is appended to the DOM ID of its vote forms to make it unique, like so (just shows the vote up button, the vote down button is similar):
<% form_for [entry, Vote.new], :html => { :id => 'new_up_vote_' + entry.id.to_s } do |f| -%>
<%= f.hidden_field :up_vote, :value => 1, :id => 'vote_up_vote_' + entry.id.to_s %>
<%= image_submit_tag('/images/icon_vote_up.png', :id => 'vote_up_vote_submit' + entry.id.to_s, :class => 'vote-button vote-up-button') %>
<% end -%>
I also had the same issue but wanted a more extensible solution than adding the ID to each field. Since we're already using the form_for ... |f| notation the trick is to change the name of the model and you get a new HTML ID prefix.
Using a variant of this method: http://www.dzone.com/snippets/create-classes-runtime (I removed the &block stuff)
I create a new model that is an exact copy of the model I want a second form for on the same page. Then use that new model to render the new form.
If the first form is using
#address = Address.new
then
create_class('AddressNew', Address)
#address_new = AddressNew.new
Your ID prefix will be 'address_new_' instead of 'address_' for the second form of the same model. When you read the form params you can create an Address model to put the values into.
For those stumbling here, looking for the solution for Rails 3.2 app, look at this question instead:
Rails: Using form_for multiple times (DOM ids)

Resources