Rails: understanding custom form helper - ruby-on-rails

new.html.erb
<%= form_for #star do |f|%>
<%= star_radio(f, true)%>
<% end %>
stars_helper.rb
module StarHelper
def star_radio(form, status)
form.label "star_#{status}", class: 'radio-inline' do
form.radio_button(:star, status) + i18n_star(status)
end
end
def i18n_star (status)
I18n.t("activerecord.attributes.star.is_sun.#{status}")
end
end
I saw a piece of code like above.
I am not familiar with custom form helper.
Could you let me know why we can use form.radio_button(:star, status) + i18n_star(status) inside a block and why we can use '+' to add text on radio buttons.
I will be appreciated if you could tell me where I can go to learn this.

form.radio_button helper returns a string and I18n.t too returns a string. So, you can concatenate them.
More details how form tag is generated
This is a code of radio_button:
https://github.com/casunlight/rails/blob/master/actionview/lib/action_view/helpers/form_helper.rb
def radio_button(object_name, method, tag_value, options = {})
Tags::RadioButton.new(object_name, method, self, tag_value, options).render
end
Look at implementation of render method
https://github.com/casunlight/rails/blob/master/actionview/lib/action_view/helpers/tags/radio_button.rb#L20
def render
options = #options.stringify_keys
options["type"] = "radio"
options["value"] = #tag_value
options["checked"] = "checked" if input_checked?(object, options)
add_default_name_and_id_for_value(#tag_value, options)
tag("input", options)
end
Tag helper generate html tag and return his as html safed string:
https://github.com/casunlight/rails/blob/master/actionview/lib/action_view/helpers/tag_helper.rb#L67
def tag(name, options = nil, open = false, escape = true)
"<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe
end

Related

In Rails ERB view, how can I prevent underscore from being converted to hyphen?

Having the following code in an ERB view:
<%= content_tag(:div, id: 'stat', data: {_var_: '_foo_'}) %>
generates the following HTML:
<div id="stat" data--var-="_foo_">
</div>
My intention is to obtain
<div id="stat" data-_var_="_foo_">
</div>
i.e. I do not want
data--var-
but instead
data-_var_
How can I achieve this, please ?
As pointed in the ActionView::Helpers::TagHelper docs:
To play nicely with JavaScript conventions sub-attributes are
dasherized. For example, a key user_id would render as data-user-id
and thus accessed as dataset.userId.
To illustrate, you can check in the Rails source code (tag_helper.rb) prefix_tag_option invoking key.to_s.dasherize:
def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
#...#
content_tag_string(name, content_or_options_with_block, options, escape)
#...#
end
def content_tag_string(name, content, options, escape = true)
tag_options = tag_options(options, escape) if options
#...#
end
def tag_options(options, escape = true)
# ...
# TAG_PREFIXES = ['aria', 'data', :aria, :data].to_set
# invoke prefix_tag_option only if it's a data- sub-attributes
if TAG_PREFIXES.include?(key) && value.is_a?(Hash)
#...#
output << prefix_tag_option(key, k, v, escape)
end
#...#
end
def prefix_tag_option(prefix, key, value, escape)
key = "#{prefix}-#{key.to_s.dasherize}"
#...#
end
If you don't want to dasherize your keys, a possible "workaround" is to set the data-attribute directly in the options hash, like this:
<%= content_tag(:div, "test", { id: 'stat', 'data-_var_': '_foo_' }) %>
This way, Rails will render:
<div id="stat" data-_var_="_foo_">test</div>

Conditionally add class to link_to with slim syntax

I have a link and the code is as follows:
= link_to 'Payment', account_payment_path, class:{'active'}
and I want to add a conditional logic to the view, so if the action_name is same, then add class active
I then change to the following code
= link_to 'Payment', account_payment_path, class:{'active' if action_name == 'payment'}
but it results in error. How can I fix it.?
If you want to get active links there is a gem build for that active_link_to, you can use it like this and it will handle adding the active class for you:
=active_link_to 'Payment', account_payment_path
for your problem you can use this:
= link_to 'Payment', account_payment_path, class: (action_name == 'payment' ? 'active' : '')
Try this ......
= link_to 'Payment', account_payment_path, :class => action_name == 'payment' ? 'active' : ''
Hope this will help you.
I'm late to the party, but here's what I think is more flexible, i.e. can use with any HTML tag whether it be a, li or anything.
# app/helpers/menu_helper.rb
module MenuHelper
class MenuBuilder
def initialize(template, active_class_name, active_value)
#template = template
#active_class_name = active_class_name
#active_valule = active_value
end
def item(value)
contents = #template.capture { yield }
return contents unless value == #active_value
body = Nokogiri::HTML.parse(contents).at('body')
# Method :+= does not always work because body.child['class'] could be nil
body.child['class'] = "#{body.child['class']} #{#active_class_name}"
body.inner_html.html_safe
end
end
def menu_for(active_class_name, active_value)
capture { yield MenuBuilder.new(self, active_class_name, active_value) }
end
end
Rails loads this helper automatically so that you can use it in views:
# app/views/shared/_left_sidebar.html.slim
aside
.menu
ul.list
= menu_for 'active', yield(:sidebar_menu_l1_active_value) do |m|
= m.item 'menu-1'
li = link_to 'Menu 1', '#'
= m.item 'menu-2'
li = link_to 'Menu 2', '#'
-
# app/views/home/index.html.slim
- content_for :sidebar_menu_l1_active_value, 'menu-1'
...

Send helper additional class

In a rails 4 app, I'm trying to pass a default option to the text_field helper, but seem to be stuck on how to implement this.
So far, I have in my view:
<%= new_text_field :name, class: "", placeholder: "" %>
and in my application_helper.rb
def new_text_field(object_name, method, options = {})
text_field(object_name, method, options = {}) # Trying to pass in a default class here, for example ".bigger"
end
Try this:
def new_text_field(object_name, method = nil, options = {})
options[:class] ||= 'bigger' # this will set bigger as default value if "class" option isn't passed
text_field(object_name, method, options = {})
end
Something like this should work:
def new_text_field_tag(name, value=nil, options)
your_class = "bigger"
if options.has_key?(:class)
options[:class] += " #{your_class}"
else
options[:class] = your_class
end
text_field_tag(name, value, options)
end

Passing params in a helper

I have, on my Miniature model view page the following code
<%= content_setmini_links_with_quantity(#miniature) %>
It refers to this miniatures_helper method
def content_setmini_links_with_quantity(miniature)
miniature.reverse_contents.map{|content| content_setmini_link_with_quantity(content)}.join(' ').html_safe
end
Which in turn references this miniatures_helper method
def content_setmini_link_with_quantity(content)
string = (tag "td"), (link_to top_pic(content.setmini_id), content.setmini), (link_to content.setmini.name, content.setmini)
string << " x#{content.quantity}" if content.quantity.present?
return string
end
This is supposed to reference the final miniatures_helper method
def top_pic
#miniature = Miniature.find(params[:setmini_id])
if #miniature.collections.first.photo != nil
image_tag(#miniature.collections.first.photo.url(:icon), :retina => true, :class => "curvediconminiset")
else
image_tag("https://system/stock/blank.gif", :retina => true, :class => "curvediconminiset")
end
end
but I can't work out how to call top_pic correctly.
As far as I can see in content_setmini_link_with_quantity(content) the only param available is content. Content.setmini_id is the param I want to use to find the #miniature to display but I can't get it to work.
Any help much appreciated.
You are calling
top_pic(content.setmini_id)
But your top_pic method definition does not have a parameter defined.
Change the definition and first line of top_pic to
def top_pic(setmini_id)
#miniature = Miniature.find(setmini_id)
and forget about using params in a helper.

Rails Button, remote_function. Possible without Ajax?

I want to create a very simple search partial. It has a text box, to query, and search db. Can I create a remote_function call without using AJAX or JS? Can I keep it entirely "Rails-ee"?
<%= text_field_tag "search_term",'', :size => 10 %>
<%= button "search", :onclick => remote_function( :url => {:action => :fill_in_lots },
:with => "search_term" ) %>
This isn't a problem, you need to use a technique called formal link. Instead of button you put a from with submit button. Below is a code of helper I use for this:
def formal_link_to(*args, &block)
options = html_options = name = nil
if block_given?
options = args.first
html_options = args.second
name = capture(&block)
else
name = args.first
options = args.second || {}
html_options = args.third
end
method = html_options.delete(:method) || "POST"
method = method.to_s.upcase
url = url_for(options)
html = "<form class=\"formal-link\" action=\"#{url}\" method=\"post\">"
html += "<input type=\"hidden\" value=\"#{form_authenticity_token}\" name=\"authenticity_token\" />"
html += "<input type=\"hidden\" value=\"#{method}\" name=\"_method\" />"
html += link_to(name, "#", html_options)
html += "</form>"
if block_given?
concat(html)
else
return html
end
end
You use this helper like a normal link_to, but you can pass extra options :method in second hash. Example:
<%= formal_link_to "Fill in lots", { :action => "fill_in_lots" }, { :method => :post } -%>
Remarks:
1. This of course will make the full page reload, but it is inevitable without using JavaScript.
2. I assumed action fill_in_lots is exposed to POST request. In case of GET you can use normal link_to helper.

Resources