Use an image for radio button label in Rails 4 - ruby

I am trying to use the helper collection_radio_buttons to show an image as a label, and want to save the image_id to the model so that I can retrieve the url, so far I have come up with this which shows a radio button and the image_url as the label.
Something I have noticed already is that I can only click the radio button once and it then just stays in its on state:
<%= f.collection_radio_buttons(:default_image_id, DefaultImage.all, :id, :image) %>
The html generated looks like so:
<input id="campaign_default_image_id_1" name="campaign[default_image_id]" type="radio" value="1" />
<label for="campaign_default_image_id_1">https://calcuttatest.s3.amazonaws.com/uploads/default_image/image/1/vandals_portfolio.png</label>
How do I set this up correctly? Can I wrap the label in a image_tag ?
Edit
So after some more research and trying to piece this together I can now render an image and a radio button, but the image is the default image I uploaded with carrierwave and not the resized :small version that I would like. Can I pass the :small version through?
<%= f.collection_radio_buttons(:default_image_id, DefaultImage.all, :id, :image) do |b| %>
<%= b.label { image_tag("#{b.text}") + b.radio_button } %>
b.text retrieves the url
https://calcuttatest.s3.amazonaws.com/uploads/default_image/image/2/ymca_portfolio.png
But I would like it to prefix the filename with fancy_box_array, like so:
https://calcuttatest.s3.amazonaws.com/uploads/default_image/image/2/fancy_box_array_ymca_portfolio.png
The html generated with this new code is:
<label for="campaign_default_image_id_1"><img alt="Vandals portfolio" src="https://calcuttatest.s3.amazonaws.com/uploads/default_image/image/1/vandals_portfolio.png" />
<input id="campaign_default_image_id_1" name="campaign[default_image_id]" type="radio" value="1" /></label>
thanks

There is a good reference on what you are trying to do. Try something like:
f.collection_radio_buttons(:default_image_id, DefaultImage.all, :id, :image) do |b|
b.label { b.radio_button + image_tag(b.object.image) }
end

Related

Autocomplete using a datalist doesn't work properly with phx-change

When an option (e.g., banana) is selected from the autocomplete list populated by a datalist, the selected option (banana) is entered into the textbox as expected, but the autocomplete list is still displayed as shown in the attached image.
On the contrary, the list disappears as expected if the event is bound by phx-submit (rather than phx-change, phx-keyup or phx-keydown). Unfortunately, I can't use phx-submit because autocomplete list should be updated as the input is made.
It appears that when an option is selected, and LiveView finishes handling phx-change and updates the page, it re-activates the autocomplete menu. How can I avoid this and make the option list disappear when an option is selected?
<form phx-change="username_change2">
<input id="username2"
type="text"
name="username2"
placeholder="Enter your name"
autofocus
autocomplete="off"
list="usernames"
value ={#username}>
</form>
<datalist id="usernames">
<%= for username <- (["apple","banana","cherry", "peach"]) do %>
<option value= { username }><%= username %></option>
<% end %>
</datalist>
def handle_event("username_change2", %{"username2" => kwd}, socket) do
IO.inspect(kwd, label: "username changed2")
socket1 = assign(socket, username1: kwd, username: kwd)
{:noreply, socket1}
end

Capybara not matching radio buttons in rspec test

I'm trying to write a test for a Bootstrap form using rspec. I have the following code:
<div id="published" class="col-sm-auto">
<div class="custom-control custom-radio custom-control-inline">
<%= form.radio_button :published, true, checked: page.published?, class: 'custom-control-input' %>
<%= form.label :published, 'Published', value: true, class: 'custom-control-label' %>
</div>
<div class="custom-control custom-radio custom-control-inline">
<%= form.radio_button :published, false, checked: !page.published?, class: 'custom-control-input' %>
<%= form.label :published, 'Hidden', value: false, class: 'custom-control-label' %>
</div>
</div>
which generates the following HTML:
<div id="published" class="col-sm-auto">
<div class="custom-control custom-radio custom-control-inline">
<input class="custom-control-input" type="radio" value="true" name="page[published]" id="page_published_true" />
<label class="custom-control-label" for="page_published_true">Published</label>
</div>
<div class="custom-control custom-radio custom-control-inline">
<input class="custom-control-input" type="radio" value="false" checked="checked" name="page[published]" id="page_published_false" />
<label class="custom-control-label" for="page_published_false">Hidden</label>
</div>
</div>
When I run this test, it fails with the message
it 'should default to unpublished' do
expect(page).to have_checked_field('Hidden')
end
giving the message expected to find visible field "page[published]" that is checked and not disabled but there were no matches. Also found "", "", which matched the selector but not all filters.
Looking at the HTML in the inspector, the field is visible and not disabled, and there is a label that matches the text. I have no idea what the two empty strings are about.
Please could somebody tell me why have_checked_field isn't matching? I'm really not keen on writing a more brittle test that uses has_css or has_xpath to look for an input tag with a specific ID — the whole point is that the user should see a field labelled "Hidden" and it should be checked, regardless of what's happening behind the scenes!
I'll 99.9% guarantee you the radio input isn't actually visible and has been replaced by an image using CSS (and maybe some JS). You could do
expect(page).to have_checked_field('Hidden', visible: false)
or you could do something slightly more complicated that verifies the label is actually visible like
expect(page).to have_css('input:checked + label', exact_text: 'Hidden')
Another solution if you're dealing with a lot of these radio buttons is to write a custom selector type something like
Capybara.add_selector(:bootstrap_radio) do
xpath do |locator|
XPath.descendant(:input)[XPath.attr(:type) == 'radio'].following_sibling(:label)[XPath.string.n.is(locator)]
end
filter(:checked) do |node, value|
node.sibling('input[type="radio"]', visible: :hidden).checked? == value
end
end
which would then allow you to do
expect(page).to have_selector(:bootstrap_radio, 'Hidden', checked: true)
and then you could write helper methods like have_checked_bootstrap_radio if wanted. Note: this code was off the cuff so the XPath/CSS expressions may not be 100% correct but the general idea is sound :)

simple-form not looking up i18n translations

simple-form is simply not looking up i18n translations. I was trying it on the placeholders.
simple_form.en.yml
en:
simple_form:
placeholders:
company:
name: "Company name"
The form:
<%= simple_form_for #company,
url: company_path(#company) do |f| %>
<div class="form-group">
<%= f.text_field :name, class: "form-control" %>
</div>
I've debugged using i18n-debug, and it didn't seem to attempt to look up
en.simple_form.placeholders.company.name
at all. Other non simple-form i18n lookups are triggering just fine.
Did I miss a step to 'turn on' i18n for simple-form? I thought it works right out of the box.
Looks like you are using text_field - a Rails form helper. Simple form supports it but it is not implemented in simple form (i.e. falls back to Rails). Change it to f.input :name and it should work.

Getting no results back from a form_for in Rails 4.0 Ruby 2.0

I am attempting to make a quiz in my rails application and I have looked around and the only gem out there that looked like it did what I want was Survey, but that is not ready for rails 4. Anyway I borrowed the structure that Survey gem creates. So I have Attempts, Surveys, Questions, and Answers
My problem is when I try to pass back which answers were picked to the attempt controller and I am not sure. I am still new to rails so it could be something I am doing / not doing.
The View looks like this
<% provide(:title, 'Quiz') %>
<h1>Quiz</h1>
<%= form_for(Attempt.new) do |f| %>
<% #survey = Survey.find(1) %>
<h3><%= #survey.description %></h3>
<br/>
<% #survey.questions.each do |question| %>
<h4><%= question.text %></h4>
<br/>
<% question.answers.each do |answer| %>
<h5><%= f.radio_button question, answer.correct?, :checked => false %> <%=answer.text%></h5>
<br/>
<% end %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
The Params currently being returned are
{"utf8"=>"✓", "authenticity_token"=>"yCp4rsZfgZDTYK32FYgXTyZSQRQ4DcTWfokbrhImI1Q=", "attempt"=>{}, "commit"=>"Create Attempt", "action"=>"create", "controller"=>"attempts"}
This is my model structure (Grouped together to make it easier to read)
Attempt has many Surveys
Survey belongs to Attempts
Survey has many Questions
Question belongs to Surveys
Question has many Answers
Answer belongs to Questions
Edited: Added in form html
<form accept-charset="UTF-8" action="/attempts" class="new_attempt" id="new_attempt" method="post"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="✓"><input name="authenticity_token" type="hidden" value="jOQCSERK6LKWwKwIprv0fhn62W+9T13WSXss8oswyFo="></div>
<h3>Tests if I can register individuals</h3>
<br>
<h4>Paul's Favorite Color</h4>
<br>
<h5><input id="attempt_#<Question:0x007f41704d11e8>_false" name="attempt[#<Question:0x007f41704d11e8>]" type="radio" value="false"> Green</h5>
<br>
<h5><input id="attempt_#<Question:0x007f41704d11e8>_false" name="attempt[#<Question:0x007f41704d11e8>]" type="radio" value="false"> Blue</h5>
<br>
<h5><input id="attempt_#<Question:0x007f41704d11e8>_true" name="attempt[#<Question:0x007f41704d11e8>]" type="radio" value="true"> Teal</h5>
<br>
<h4>Paul's Age</h4>
<br>
<h5><input id="attempt_#<Question:0x007f41704d0b80>_false" name="attempt[#<Question:0x007f41704d0b80>]" type="radio" value="false"> 20</h5>
<br>
<h5><input id="attempt_#<Question:0x007f41704d0b80>_true" name="attempt[#<Question:0x007f41704d0b80>]" type="radio" value="true"> 21</h5>
<br>
<h5><input id="attempt_#<Question:0x007f41704d0b80>_false" name="attempt[#<Question:0x007f41704d0b80>]" type="radio" value="false"> 22</h5>
<br>
<div class="actions">
<input name="commit" type="submit" value="Create Attempt">
</div>
</form>
Create an Attempt instance variable in AttemptsConroller on creation:
def new
#attempt = Attempt.new
end
In your view use the instance just created:
<%= form_for(#attempt) do |f| %>
That should do the trick if I did not completely miss the problem at hand.
The HTML you posted really makes it clear that something "un-railsish" is going on here. If you call the form_for-helper method in the way you did it it will create a FormBuilder object which will build the actual form, so we have to check the parameters of the radio_button in the FormBuilder class (in Rubyspeak that might also be FormBuilder#radio_button). There we find
radio_button(method, tag_value, options = {})
so the three parameters of the method rails expects are the name of the method of the object the form should operate on (which in your case would be your Attempt.new object). The most usual thing to pass at this point is a symbol with the method name, but as you provided a Question-object rails is trying to be forgiving and converts whatever it is passed into a String (probably using the to_s method). As you do not seem to have implemented to_s in your Question class the default implementation of Object will kick in to provide a "description" of your object with a class name and an address in angular brackets. As the angular brackets would confuse the HTML-parser these are gently escaped for you and you end up with messed up id and name attributes of your input elements. This will most certainly confuse the rails params parser when the HTTP POST-request is passed back and you end up with what you posted.
It is not really obvious how to fix this without knowing more about your Model classes, but most probably you want to use a nested form to solve this in the right way. This is not something I can do from the top of my head, but you can check out this RailsCast for more info.

simple_form's collection_radio_button and custom label class

I'm trying to make a star rating form with radio collection using FontAwesome, for this I actually need to change the label classes of the collection_radio_button input generated by simple_form but can't find any obvious solution.
So far I use:
form_for #user do |f|
f.collection_radio_buttons :rating, [[1, 'Bad'] ,[2, 'Ok'], [3, 'Great']],
:first, :last, { item_wrapper_tag: false }
end
Which generates:
<input id="review_rating_1" name="review[rating]" type="radio" value="1" />
<label class="collection_radio_buttons" for="review_rating_1">Bad</label>
<input id="review_rating_2" name="review[rating]" type="radio" value="2" />
<label class="collection_radio_buttons" for="review_rating_2">Ok</label>
<input id="review_rating_3" name="user[options]" type="radio" value="3" />
<label class="collection_radio_buttons" for="review_rating_3">Great</label>
But I'd like the labels to have an extra class, like:
<input id="review_rating_3" name="user[options]" type="radio" value="3" />
<label class="collection_radio_buttons icon-star" for="review_rating_3">Great</label>
UPDATE: This class is defined statically at: https://github.com/plataformatec/simple_form/blob/master/lib/simple_form/tags.rb#L43
This can be achieved by using a block:
form_for #user do |f|
f.collection_radio_buttons :rating, [[1, 'Bad'] ,[2, 'Ok'], [3, 'Great']],
:first, :last, { item_wrapper_tag: false } do |b|
b.radio_button + b.label(:class => "collection_radio_buttons icon-star")
end
end
This doc can showcase some other example: http://rubydoc.info/github/plataformatec/simple_form/SimpleForm/FormBuilder:collection_radio_buttons
In case anyone wonders how to add class to label wrapping radiobutton input when you set boolean_style = :nested here is what I came with:
You can set option called :item_label_class when calling your input, f.e.:
<%= f.input :type, as: :radio_buttons,
collection: Listing::TYPES.map{ |type| [Listing.translate_type(type), type] },
label: false,
item_label_class: 'radio' %>
I wanted to automate this stuff, so I defined custom CollectionRadioButtonsInput class. You need to add to method apply_default_collection_options!:
def apply_default_collection_options!(options)
super(options)
options[:item_label_class] == 'radio' if input_type == :radio_buttons
end

Resources