simple_form - can't generate combined select input - ruby

I have complex form with a lot of controls, and I'm currently using simple_form gem due to its flexibility. But when I wanted to do something more complex I faced to several problems that currently looking obscure for me. I would like to introduce combined collection input, that will render both optgroups and single non-groupped selects. Generated html that I want to achieve should look something like this:
<select name="select" multiple="multiple">
<option value="1">Milk</option>
<optgroup label="Soda">
<option value="2">Cola</option>
<option value="3">Fanta</option>
</optgroup>
</select>
I've tried to create custom input class, but stucked on implementation details of input method, I simply couldn't find out how to generate proper output.
UPDATE
Currently quick and dirty implementation for custom input looks something like this, but I don't think that it is a good idea to drop all goodnesses with options that simple_form gives me.
class CombinedMultiselectInput < SimpleForm::Inputs::CollectionSelectInput
include ActionView::Helpers::FormTagHelper
include ActionView::Helpers::FormOptionsHelper
def input
out = ActiveSupport::SafeBuffer.new
option_tags = ungrouped_options.safe_concat(grouped_options)
out << select_tag(options[:name], option_tags, class: ['select', 'form-control'])
out
end
private
def ungrouped_options
# this can be retrieved from general collection like collection[:ungrouped]
collection = [["Foo", 2], ["Bar", 3]]
options_for_select(collection)
end
def grouped_options
# and this using collection[:grouped]
collection = [["Group", [["Foobar", 4]]]]
grouped_options_for_select(collection)
end
end

Using your current design you can combine the options_for_select and option_groups_from_collection_for_select methods like so.
def ungrouped_options
[["Foo", 2], ["Bar", 3]]
end
def grouped_options
[["Group", [["Foobar", 4]]]]
end
def your_hash
{"ungrouped" => ungrouped_options, "grouped" => grouped_options}
end
Then in your view something like this should work:
<%= content_tag(:select,nil,{multiple: true,name: "select"}) do
<%= your_hash.each do |k,v| %>
<% if k == "ungrouped" %>
<%= options_for_select(v) %>
<% else %>
#this works because:
# last will be the collection of children for a member
# first will be the group name
# last on the child will be the value method
# first on the child will be the text displayed
<%= option_groups_from_collection_for_select(v, :last, :first, :last, :first) %>
<% end %>
<% end %>
<% end %>
This will create the following:
<select name=\"select\" multiple=\"true\">
<option value=\"2\">Foo</option>
<option value=\"3\">Bar</option>
<optgroup label=\"Group\">
<option value=\"4\">Foobar</option>
</optgroup>
</select>
Obviously this was simplified to show how this could be done but hopefully this points you in the right direction.
You should also be able to wrap this for simple_form although I have not tested it.
<%= f.input :some_attribute do %>
<%= f.select :some_attribute do %>
<%= your_hash.each do |k,v| %>
<% if k == "ungrouped" %>
<%= options_for_select(v) %>
<% else %>
<%= option_groups_from_collection_for_select(v, :last, :first, :last, :first) %>
<% end %>
<% end %>
<% end %>
<% end %>

Related

Ruby .each but need to add in a unique class for each of them

i have the following piece of code that loops through and shows the results however i want to be able to set a class for each of the <td> and unsure how to do this as i dont know ruby and doing the frontend on a ruby project.
<% priority[1].each do|val|%>
<td><%=val[1] %></td>
<% end %>
It just shows 4 of them so if i can set defined classes great but if not something like class1, class2, class3, class4 would be fine i guess.
Many Thanks
You could do this:
<% priority[1].each_with_index do |val,i| %>
<td class="class<%= i %>"><%= val[1] %></td>
<% end %>
Which will give every element a unique class of 'class1','class2' etc for all i.
Marginally more complicated, but this example allows you assign your own class names:
<% arr = ['Hi','Hello','Howdy','Yo'] #class names %>
<% priority[1].each_with_index do |val,i| %>
<td class="<%= arr[i] %>"><%= val[1] %></td>
<% end %>

Ruby each, different action depending on index, with a pattern?

Ok so my title may be confusing. What I want to do is to loop through a collection of models, and for the first two, render a template, for the next four render a different template and next two render the same template as for the first ones, and so on.
Like this:
<% ads.each do |ad| %>
<% # if it's 1-2, 7-8, 13-14 and so on render as big' %>
<%= render 'front/home/big_ad', ad: ad %>
<% # if it's 3-6, 9-12, 15-18 and so on render as small' %>
<%= render 'front/home/small_ad', ad: ad %>
<% end %>
<% end %>
What would be the cleanest way to accomplish this?
If your groups would be even long, then you could use in_groups_of command, and alternate between them, but with these specifications, the easiest way is this:
<% ads.each_with_index do |ad, index| %>
<% if (index % 6 < 2) %>
<%= render 'front/home/big_ad', ad: ad %>
<% else %>
<%= render 'front/home/small_ad', ad: ad %>
<% end %>
<% end %>

How to create a sequence of numbers in a Ruby on Rails form

I have 10 form fields and I want to append a number next to each field, going sequentially from 1 to 10. The problem is that the code is already in a loop. It's inside a partial, and the partial is passed a collection.
<%= fields_for "list", f do |f| %>
<!-- I want 1, 2, etc to appear here depending on the iteration. -->
<%= f.label :name %>
I tried using <%= i += 1 %> but it does not work since i is not defined. If I define i, it will keep getting reset to the same number, so it makes no difference. Any ideas?
Rails automatically defines a local variable called partialname_counter where, obviously, "partialname" is the name of your partial. So if your partial is called e.g. _list_item.html.erb you could write it like this:
<%= fields_for "list", f do |f| %>
<%= list_item_counter + 1 %>
<%= f.label :name %>
<% end %>
(The + 1, of course, is there because the counter starts at 0.)
Another option would be to just let the browser do the numbering for you using an ordered list:
In the view:
<ol>
<%= render :list_item, :collection => #some_items %>
</ol>
...and in the partial:
<%= fields_for "list", f do |f| %>
<li>
<%= f.label :name %>
</li>
<% end %>
This option is probably more semantic, and lists are easy to style in CSS.

Sorting and manipulating a hash in Ruby

In my rails 3.1 project I have a Book model that has a ID, NAME, and BOOK_ORDER. I am using the ranked-model gem, which for its sorting process creates large numbers in the sorting(:book_order) column. Im looking for some help to create a method to sort all of the Books by the :book_order column, then simplify the :book_order numbers.
So, I have this:
controller
#books = Books.all
view
<% #books.each do |book| %>
<%= book.book_order %>
<% end %>
# book1.book_order => 1231654
# book2.book_order => 9255654
# book3.book_order => 1654
But want this:
view
<% #books.each do |book| %>
<%= book.clean_book_order %>
<% end %>
# book1.clean_book_order => 2
# book2.clean_book_order => 3
# book3.clean_book_order => 1
Additionally, i don’t want to change the database entry, just use its current values to make simpler ones.
Thanks!
UPDATE:
Thanks to nash’s response I was able to find a solution:
In my Book Model I added the clean_book_order method:
class Book < ActiveRecord::Base
include RankedModel
ranks :book_order
def clean_book_order
self.class.where("book_order < ?", book_order).count + 1
end
end
<% #books.each do |book| %>
<%= book.book_order_position %>
<% end %>
EDIT:
Oh, I see. https://github.com/harvesthq/ranked-model/issues/10

hash enumeration and using outside arguments

I have a form builder and the normal |f| variable. In the middle I'd like to iterate over some hash and get a list of keys by which to build the form. How can I incorporate "f" as an argument to the enumerator.
<%= form_for ..... do |f| %>
<% available_types.each do |k,v| %>
<%= f.text_filed :selection, :value => v %>
<% end %>
<% end %>
As you can see in the code above, "f" is outside of the scope. Ideas?
f should still be in the scope. You misspelled text_field though.

Resources