Rails Validation | add class to surrounding <div> - ruby-on-rails

Rails question. The default behavior for error validation is to fieldWithError-styled div around the input field like the following
<div class="type-text" id="pre_negotiation_value_div">
<label for="contract_budget_holder">Budget Holder</label>
<div class="fieldWithErrors">
<input id="contract_budget_holder"name="contract[budget_holder]" size="30" type="text" value="" />
</div>
</div>
What I am trying to accomplish is, to surround the label AND the input field with a classed div tag, so to look like the following:
<div class="type-text fieldWithErrors" id="pre_negotiation_value_div">
<label for="contract_budget_holder">Budget Holder</label>
<input id="contract_budget_holder"name="contract[budget_holder]" size="30" type="text" value="" />
</div>
Any ideas how to accomplish this? Can you hook up javascript into ActionView::Base.field_error_proc?
I am new to Rails, I apologize if this is super easy!
Thanks for your help.
Jonathan

It's not super easy to do exactly what you want, but if you have ERB like this:
<div id="pre_negotiation_value_div" class="type-text">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
You will get HTML like this:
<div id="pre_negotiation_value_div" class="type-text">
<div class="fieldWithErrors"><label for="foo_name">Name</label></div>
<div class="fieldWithErrors"><input id="foo_name" name="foo[name]" size="30" type="text" value="" />
</div>
Where both the label and the text_field will have the fieldWithErrors div around them. Depending on how you want to style it, that might be good enough. If it's not good enough, you'll have to do a custom helper like this:
class ActionView::Helpers::FormBuilder
def labeled_input(method, options={}, &block)
(options[:class] ||= "") << " fieldWithErrors" if #object.errors.on(method)
ActionView::Helpers::InstanceTag.send(:alias_method, :original_error_wrapping, :error_wrapping)
ActionView::Helpers::InstanceTag.send(:define_method, :error_wrapping,
Proc.new {|html_tag, has_error| html_tag})
#template.concat(#template.content_tag(:div, #template.capture(&block), options))
ensure
ActionView::Helpers::InstanceTag.send(:alias_method, :error_wrapping, :original_error_wrapping)
ActionView::Helpers::InstanceTag.send(:remove_method, :original_error_wrapping)
end
end
Put that in config/initializers/labeled_input.rb. It's a bunch Ruby meta-foo, but what it does is put the "fieldWithErrors" class on the outer div. It temporarily re-defines the error_wrapping method of InstanceTag so that the inner label and text_field tags don't have the "fieldWithErrors" div surrounding them. You use it in ERB like this:
<% f.labeled_input :name, :id => "pre_negotiation_value_div", :class => "type-text" do %>
<%= f.label :name %>
<%= f.text_field :name %>
<% end %>

RAILS 3 UPDATE
I'm updating my app to rails 3. If you've used this method and are upgrading you'll get some deprecation warnings and form fields appearing twice or several times.
To fix these:
1) Change the line in labeled_input.rb that checks for errors. This stops deprecation warnings accessing errors:
(options[:class] ||= "") << " fieldWithErrors" unless #object.errors[method].empty?
2) Change the line in labeled_input.rb before the 'ensure'. This stops form elements appearing multiple times:
#template.content_tag(:div, #template.capture(&block), options)
3) In your view files where you call labeled_input, use <%= rather than <%. This prevents deprecation warnings.
Hope that saves you some time.

Related

How do I hide the form title that appears inside a legend tag when generating check boxes for an association using simple-form?

I'm using simple-form (Ruby 2.5.1, Rails 5.2.3, simple-form 4.1.0) to generate checkboxes for an association. The form element it generates includes an overall title for the section that defaults to the name of the association (in this case, "Menu item tags") and is inside a tag. I want to hide it completely, but can't.
I can change the text to "Test" using label: "Test", but label: false doesn't hide it like I'd expect. I've read the docs and done my best to read the source, but I can't make any progress.
Here's the simple-form call:
f.association :menu_item_tags, as: :check_boxes
And here's the HTML output:
<fieldset class="form-group check_boxes optional listing_menus_menu_sections_menu_items_menu_item_tags form-group-valid">
<legend class="col-form-label pt-0">Menu item tags</legend>
<input type="hidden" name="listing[menus_attributes][0][menu_sections_attributes][0][menu_items_attributes][0][menu_item_tag_ids][]" value="">
<div class="form-check">
<input class="form-check-input is-valid check_boxes optional" type="checkbox" value="1" checked="checked" name="listing[menus_attributes][0][menu_sections_attributes][0][menu_items_attributes][0][menu_item_tag_ids][]" id="listing_menus_attributes_0_menu_sections_attributes_0_menu_items_attributes_0_menu_item_tag_ids_1">
<label class="collection_check_boxes" for="listing_menus_attributes_0_menu_sections_attributes_0_menu_items_attributes_0_menu_item_tag_ids_1">Vegetarian</label>
</div>
</fieldset>
I need to remove the tag on the second line of the HTML. label: false seems like the obvious convention, but it doesn't work.
Edit: This is not a duplicate of Remove outer label from collection_check_boxes. They're using a different function, getting different HTML out, and describing a different problem (that everything is wrapped in a label element). And the solution doesn't fix or affect the behavior I'm describing.
Edit: Here's a simplified version of the form in question, in response to a comment below:
<%= simple_form_for #listing do |f| %>
<%= f.simple_fields_for :menus do |f| %>
<%= f.simple_fields_for :menu_sections do |f| %>
<%= f.simple_fields_for :menu_items do |f| %>
<%= f.association :menu_item_tags, as: :check_boxes %>
<% end %>
<% end %>
<% end %>
<% end %>
= f.association :menu_item_tags, as: :check_boxes, legend_tag: false
You can eliminate the legend that is created by simple form with label: ""
You can do the following in css:
form[name="your_form_name"]>fieldset>legend {
display: none;
}
But it's purely cosmetic
;)
According to documentation:
label: false
should work to hide the generated <legend> element.
I tested this and found that this in fact does not work.
The workaround I found to work was:
label: ''
The <legend> element will still be generated but it will be blank.

Defining field label in a block - on error, field_with_errors div tag not in right place

I am using validates_acceptance_of :terms, :message => "must be accepted" in my user.rb model, and am using bootstrap-sass.
My check box code looks like this in the view:
<div class="control-group">
<%= f.label :terms, :class => "control-label" do %>
Accept <%= link_to('Terms of Use *', "#myTOUModal", :"data-toggle" => "modal") %>
<% end %>
<div class="controls">
<%= f.check_box :terms %>
</div>
</div>
For some reason, when the terms check box isn't selected on form submission, the appropriate error message shows up at the top of the form, but there is a problem with the field_with_errors div class wrapping around the check box label.
The HTML for the rendered page looks like this:
<div class="control-group">
<label class="control-label" for="user_terms">
Accept Terms of Use *
</label>
<div class="controls">
<input name="user[terms]" type="hidden" value="0" />
<div class="field_with_errors">
<input id="user_terms" name="user[terms]" type="checkbox" value="1" />
</div>
</div>
</div>
The result is that the check box field label isn't highlighted on error. Is there a way to force the div tag placement for the field_with_errors class to show up just after the <div class="control-group"> tag? Why does using a block to define a field label throw off the field_with_errors tag placement? Does anyone have experience with this?
Thank you
This is a bug i think. The problem is in block. Define your label without block and everything works.
Try something like:
<% modal_html = capture do >
Accept <%= link_to('Terms of Use *', "#myTOUModal", :"data-toggle" => "modal") %>
<% end %>
<%= f.label :terms, modal_html, :class => "control-label" %>
Or helper:
def modal_html
#Q{Accept #{link_to('Terms of Use *', "#myTOUModal", :"data-toggle" => "modal")} }.html_safe
end

How to change my custom error html from rendering itself twice?

When I try to create a category by using Ajax I get a strange behavior with my error message.
Right now I have my error message appearing like this:
My create.js.erb and new.js.erb both have the same code which is just this line:
$(".cc-form").html("<%= escape_javascript(render(:partial => 'categories/form', locals: { category: #category })) %>");
This is my category form:
<%= form_for(#category, :remote => true, :html => { :class => "add-form", :id => "cform" }) do |f| %>
<fieldset>
<p>
<%= f.label :name, "Category Name *" %>
<br />
<%= f.text_field :name %>
</p>
<div class="form-actions">
<%= f.submit "Create" %>
</div>
</fieldset>
Here is the code to enable custom error HTML:
# application.rb
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
errors = Array(instance.error_message).join(',')
%(#{html_tag}<span class="validation-error"> #{errors}</span>).html_safe
end
Then the HTML itself along with the error HTML:
<p>
<label for="category_name">Category Name *</label><span class="validation-error"> can't be blank</span>
<br>
<input id="category_name" name="category[name]" size="30" type="text" value=""><span class="validation-error"> can't be blank</span>
</p>
I only want the error message next to the label and not the right side of the input. How would I do this? The format is tricky for me when I look at it.
Thanks.
I guess you use field_error_proc badly, because it's good to wrap the input field into element with special class. Field_error_proc tags both the label and input tag with field_with_error by default since the refered object (:name that means category.name having the errors array) is the same. It's not a defect, because it's good for changing your label's color to red in this case. I checked the Rails code (actionpack/lib/action_view/helpers/tags/label.rb) and you can't turn this behavior off for labels (it can be a feature request), so I guess there's only one solution for your problem if you use plain html for labels.
I have two solutions now for your problem:
Instead of
<%= f.label :name, "Category Name *" %>
use
<label for="category_name">Category Name *</label>
in your view.
It's not too nice, but you can use f.label if you make form .label .validation-error { display: none } in your stylesheet file. I know it's just a workaround, but if f.label is necessary, then I don't know better solution.
You have two of these <span> tags in the page:
<span class="validation-error"> can't be blank</span>
EDIT:
Okay, it looks like both the label and the input need to be wrapped with a custom error. Here are a couple of links:
http://stackoverflow.com/questions/5267998/rails-3-field-with-errors-wrapper-changes-the-page-appearance-how-to-avoid-t
https://gist.github.com/1464315

Ruby on rails tag functions. Why does the case of radio_button_tag.id not match label.for?

While registering for openstreetmap, on the terms page, I noticed that clicking the labels didn't check the radio buttons associated with them. Here is the html:
<!-- legale is GB -->
<form action="/user/terms" method="post">
<p>
Please select your country of residence:
<input id="legale_fr" name="legale"
onchange="Element.update("contributorTerms", "<img alt=\"Searching\" src=\"/images/searching.gif?1313181320\" />");; new Ajax.Request('/user/terms?legale=FR', {asynchronous:true, evalScripts:true})"
type="radio" value="FR" />
<label for="legale_FR">France</label>
<input id="legale_it" name="legale" ... type="radio" value="IT" />
<label for="legale_IT">Italy</label>
<input checked="checked"
id="legale_gb" name="legale" ... type="radio" value="GB" />
<label for="legale_GB">Rest of the world</label>
</p>
</form>
As you can see the checkbox id="legale_gb" doesn't match the label for="legale_GB".
Now openstreetmap's website is actually open source so we can read the terms.html.erb:
<!-- legale is <%= #legale %> -->
<% form_tag :action => 'terms' do %>
<p>
<%= t 'user.terms.legale_select' %>
<% [['france', 'FR'], ['italy', 'IT'], ['rest_of_world', 'GB']].each do |name,legale| %>
<%=
radio_button_tag 'legale', legale, #legale == legale,
:onchange => remote_function(
:before => update_page do |page|
page.replace_html 'contributorTerms', image_tag('searching.gif')
end,
:url => {:legale => legale}
)
%>
<%= label_tag "legale_#{legale}", t('user.terms.legale_names.' + name) %>
<% end %>
</p>
<% end %>
I'm a rails newbie, but I can't see anything there that lowercases the id of the radio button tag. What's more, even when I look at the source of radio_button_tag, sanitize_to_id I can't see where this is coming from.
Anyone got any idea what's causing this?
Edit Swapped out label for radio in my description according to answer from
2 things:
Wrong tag, the offender is radio_button_tag (it's capped as expected in the label).
Seems like you're linking to the wrong Rails. According to this project's environment.rb, it's using Rails 2.3.14. If you look at radio_button_tag for that release, you'll see the culprit.
# our pretty tag value is downcased on line 318
pretty_tag_value = value.to_s.gsub(/\s/, "_").gsub(/(?!-)\W/, "").downcase
# although the pretty name is not, oddly
pretty_name = name.to_s.gsub(/\[/, "_").gsub(/\]/, "")
# then the two are combined into the HTML id
html_options = { ..., "id" => "#{pretty_name}_#{pretty_tag_value}", ... }

Get data from html form to ruby in Ruby on Rails

Get data from html form to ruby in Ruby on Rails
I have some html like this
<html>
<h1>Text to PDF</h1>
<textarea name="comments" cols="40" rows="5">
Enter your Text here...
</textarea><br>
<input type="submit" value="Submit" />
</form>
</body>
</html>
I want to give the value of the text into the controller for this page/view.
How do I do this with rails?
I am new to rails, what is the mechanism for this? I don't need to write to the database just want to hand it to the controller. If there is a good tutorial for this sort of thing that would be great, I am not convince I am approaching this correctly.
You can use params['comments'] in your controller to get the value.
In your controller-
def parse_comments
comments_from_form = params['myform']['comments']
#do your stuff with comments_from_form here
end
In your view-
<h1>Text to PDF </h1>
<%= form_tag :action => 'parse_comments' do %>
<%= text_area :myform, :comments, :cols => '40', :rows => '5' %>
<%= submit_tag "Submit" %>
<% end %>
(edit: added = to form_tag opening, without it code won't work)

Resources