Send helper additional class - ruby-on-rails

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

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>

Rails: understanding custom form helper

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

Rails: refactor code with blocks

I use Rails 4.2 and i want to refactor my helper method to get rid of duplicate code:
In app/helpers/admin/tasks_helper.rb
def chosen_select(name, method, chzes, selected = nil, options = {}, html_options = {})
options[:value_method] ||= :id
options[:text_method] ||= :name
if options.key?(:placeholder)
html_options['data-placeholder'.intern] = options[:placeholder]
options.delete(:placeholder)
end
if html_options.key?(:class)
html_options[:class] = 'chosen-select ' + html_options[:class]
else
html_options[:class] = 'chosen-select'
end
chzes = options_from_collection_for_select(chzes, options[:value_method], options[:text_method], selected)
options.delete(:value_method)
options.delete(:text_method)
select(name, method, chzes, options.merge!(include_hidden: false), html_options)
end
def chosen_select_array(name, method, chzes, selected = nil, options = {}, html_options = {})
options[:value_method] ||= :id
options[:text_method] ||= :name
if options.key?(:placeholder)
html_options['data-placeholder'.intern] = options[:placeholder]
options.delete(:placeholder)
end
if html_options.key?(:class)
html_options[:class] = 'chosen-select ' + html_options[:class]
else
html_options[:class] = 'chosen-select'
end
chzes = options_for_select(chzes, selected)
options.delete(:value_method)
options.delete(:text_method)
select(name, method, chzes, options.merge!(include_hidden: false), html_options)
end
I my view i have a lot of method calls like app/views/admin/tasks/index.html.erb
<%= chosen_select(:select, :project_id, [TaskFilterOptgroups.active_projects, TaskFilterOptgroups.inactive_projects] , #task_filter_configuration.project_id, {:include_blank => true, :placeholder => 'Project'}, {'data-last-project_id' => #task_filter_configuration.project_id, :style => 'width: 150px;'}) %>
so that i don't want to change my method calls in the view.
My attempt is to make a generic method "chosen_select_generic" that will be called from the specific method like "chosen_select":
def chosen_select_generic(name, method, chzes, selected = nil, options = {}, html_options = {})
options[:value_method] ||= :id
options[:text_method] ||= :name
if options.key?(:placeholder)
html_options['data-placeholder'.intern] = options[:placeholder]
options.delete(:placeholder)
end
if html_options.key?(:class)
html_options[:class] = 'chosen-select ' + html_options[:class]
else
html_options[:class] = 'chosen-select'
end
# 2 different chzes in 2 methods:
# 1) chosen_select(...)
# chzes = options_from_collection_for_select(chzes, options[:value_method], options[:text_method], selected)
# 2) chosen_select_array(...)
# chzes = options_for_select(chzes, selected)
yield chzes
options.delete(:value_method)
options.delete(:text_method)
select(name, method, chzes, options.merge!(include_hidden: false), html_options)
end
and then chosen_select could look like:
def chosen_select(name, method, chzes, selected = nil, options = {}, html_options = {})
chosen_select_generic(name, method, chzes, selected = nil, options = {}, html_options = {}) do |contents|
chzes = option_groups_from_collection_for_select(chzes, :entries, :status, options[:value_method], options[:text_method], selected)
end
end
But this doesn't work. How can i extract the duplicate code in a block without changing the method calls in the view?
This assignment in your block won't do what you think:
chzes = option_groups_from_collection_for_select(...)
It creates a new local variable instead of changing external one. If it's the only changeable piece here, then you can just return it from the block:
chosen_select_generic(name, method, chzes, selected = nil, options = {}, html_options = {}) do |chzes|
option_groups_from_collection_for_select(chzes, :entries, :status, options[:value_method], options[:text_method], selected)
end
And receive the value in your generic method like this:
chzes = yield(chzes)

Rails: add field to all forms

Is it possible to add hidden field to all form tags?
I'm trying to do it in following way:
module ActionView::Helpers::FormTagHelper
def form_tag(url_for_options = {}, options = {}, &block)
html_options = html_options_for_form(url_for_options, options)
if block_given?
f = form_tag_in_block(html_options, &block)
else
f = form_tag_html(html_options)
end
hidden_f = ActiveSupport::SafeBuffer.new "<input name='n' type='hidden' value='v' /><\/form>"
f.gsub!(/<\/form>/, hidden_f)
f
end
end
But server shows the error:
ActionView::Template::Error (Could not concatenate to the buffer because it is not html safe.):
How should i do it?
It might be simpler to redefine the extra_tags_for_form method, which is used to add the _method, utf8, and authenticity_token hidden fields. Something like this could work:
module ActionView::Helpers::FormTagHelper
alias_method :orig_extra_tags_for_form, :extra_tags_for_form
def extra_tags_for_form(html_options)
orig_tags = orig_extra_tags_for_form(html_options)
orig_tags << "<input name='n' type='hidden' value='v' /><\/form>".html_safe
end
end
Since this advice involves redefining a private method, you will need to be sure to test it carefully any time you upgrade Rails.
Try with
module ActionView::Helpers::FormTagHelper
def form_tag(url_for_options = {}, options = {}, &block)
html_options = html_options_for_form(url_for_options, options)
if block_given?
f = form_tag_in_block(html_options, &block)
else
f = form_tag_html(html_options)
end
hidden_f = ActiveSupport::SafeBuffer.new "<input name='n' type='hidden' value='v' /><\/form>"
f.gsub!(/<\/form>/, hidden_f)
f.html_safe
end
end
gsub! taints your string with HTML unsafeness.

Rails 3 - Custom link_to helper (with default class and ability to add classes)

I'm trying to hook up a custom helper that has a default class 'pjax' but also retains an ability to add classes where need be.
Example:
link_to_pjax('pagename', page_path, :class => 'current')
So the helper would add the 'pjax' by default, and also the class 'current', or whatever is passed in.
def link_to_pjax(name, path, options = {:class => 'pjax'})
link_to(name, path, options)
end
The syntax is freaking me out. Any advice would be much appreciated.
def link_to_pjax(name, path, options)
options[:class] += ' pjax'
link_to(name, path, options)
end
edit
After test, it's much less elegant:
def link_to_pjax(name, path, options = {})
options[:class] ? options[:class] += ' pjax' : options[:class] = 'pjax'
link_to(name, path, options)
end
My first solution works but only if you have still specified a class.
The latest works in all cases:
link_to_pjax 'click me', my_super_path, class: 'ahah', id: 'hello'
link_to_pjax 'click me', my_super_path
etc
My bad...
def link_to_pjax(name, path, options={})
default_options = { :class => "pjax" }
link_to(name, path, options.merge(default_options))
end
I improved Delba answer to handle block version of link_to:
def link_to_pjax(*args, &block)
if block_given?
options = args.first || {}
html_options = args.second
link_to_pjax(capture(&block), options, html_options)
else
name = args[0]
options = args[1] || {}
html_options = args[2] || {}
html_options[:class] ? html_options[:class] += ' pjax' : html_options[:class] = 'pjax'
link_to(name, options, html_options)
end
end

Resources