Custom form helpers
Yes, you can add to the FormBuilder class and get access to the object passed into the form_for. I've done this for a lot of things: dates, times, measurements, etc. Heres an example:
class ActionView::Helpers::FormBuilder
include ActionView::Helpers::TagHelper
include ActionView::Helpers::FormTagHelper
include ActionView::Helpers::FormOptionsHelper
include ActionView::Helpers::CaptureHelper
include ActionView::Helpers::AssetTagHelper
# Accepts an int and displays a smiley based on >, <, or = 0
def smile_tag(method, options = {})
value = @object.nil? ? 0 : @object.send(method).to_i
options[:id] = field_id(method,options[:index])
smiley = ":-|"
if value > 0
smiley = ":-)"
elsif smiley < 0
smiley = ":-("
end
return text_field_tag(field_name(method,options[:index]),options) + smiley
end
def field_name(label,index=nil)
output = index ? "[#{index}]" : ''
return @object_name + output + "[#{label}]"
end
def field_id(label,index=nil)
output = index ? "_#{index}" : ''
return @object_name + output + "_#{label}"
end
end
Which you can use like this:
<% form_for @quiz do |f| %>
<%= f.smile_tag(:score) %>
<% end %>
There are some instance variables created by Rails that you can access in these helper methods:
- @object - the model object specified by the form
- @object_name - the class name of the object
- @template - I think its an instance of the ActionView, you can possibly bypass all the includes I added by calling methods on the template. Haven't tried that yet.
- @options - options passed to the FormBuilder when its created by the form_for call
I wrote the field_id and field_name methods to create these attributes on the HTML input elements the same way the regular helpers do, I'm sure there is a way to tie into the same methods that Rails uses, but I haven't found it yet.
The sky is the limit on what you can do with these helper methods, they simply return strings. You can create entire HTML tables or pages in one, but you better have a good reason to.
This file should be added in the app/helpers folder
Rails: understanding custom form helper
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
Trouble setting up Rails custom form helper method
I went back to the docs for check_box_tag
, and understood it a bit more. I ended up going with this:
module FormHelper
def wrapped_check_box_tag(name, value=1, checked=false, options={})
raw "<div class='checkbox-wrapper'>" + \
check_box_tag(name, value, checked, options) + \
"<span class='checkbox-checks'></span>" + \
"</div>"
end
class ActionView::Helpers::FormBuilder
include FormHelper
include ActionView::Helpers::FormTagHelper
include ActionView::Helpers::FormOptionsHelper
def wrapped_check_box(name, options = {})
wrapped_check_box_tag("#{@object_name}[#{name.to_s}]", options[:value], options[:checked], options)
end
end
end
This now makes a global method, as well as a form helper available:
<%= wrapped_check_box_tag :accepts_terms %>
<%= f.wrapped_check_box :receives_updates, { checked: true } %>
If anyone has any suggestions, I'm all ears!
How to write a custom form helper template for dynamically generated content?
The Twitter Bootstrap structure needs to be accurate.
Wrap the btn-group
class around the inputRadioGroup
helper like so:
<div class="btn-group" data-toggle="buttons">
@helper.inputRadioGroup(
answerRadioForm("Answer"),
options = answerList.map(answer => answer.answerID.toString -> answer.answerText),
'_label -> "Answer",
'_error -> answerRadioForm("answerID").error.map(_.withMessage("select answer")))
</div>
Then replace the template with:
@(elements: helper.FieldElements)
<label class="btn btn-primary">
@elements.input @elements.label
</label>
<span class="errors">@elements.errors.mkString(", ")</span>
<span class="help">@elements.infos.mkString(", ")</span>
In general, perhaps it'd be of interest another way of doing it. When you use fooForm("myField"...)
, you can use fooForm("myField[@i]"...)
in a for
loop where @i
is a counter going from 0 to however many inputs there are. Then you can yourself sketch out the full HTML instead of using implicit values.
By the way, the documentation with the Scala version about all this has lots more information than the Java version. See here. It has more information on inputRadioGroup
than the Java version of the documentation but still very useful reading for better understanding of all this.
Some Play Bootstrap plugin has code available on GitHub that is also useful reading especially as it uses implicit values also.
UPDATE
Here's a couple of GitHub projects showing how to achieve this:
- with Scala version of Play: https://github.com/bjfletcher/play-bootstrap-radio-group
- with Java version of Play: https://github.com/bjfletcher/play-java-bootstrap-radio-group
Screenshot of the result:
How to register custom form view helper in Zend Framework 3
Taking an example from an active project at the company I work at. We also have some default ZF3 Form ViewHelpers overwritten with our own to interface with the front-end framework. Theme name is "Alpha" (I think ;-) )
We use the following:
'view_helpers' => [
// other stuff
'invokables' => [
'Zend\Form\View\Helper\FormCollection' => AlphaCollection::class,
'Zend\Form\View\Helper\Form' => AlphaForm::class,
'Zend\Form\View\Helper\FormRow' => AlphaRow::class,
'Zend\Form\View\Helper\FormSelect' => AlphaSelect::class,
],
],
View helper itself:
// Namespace + use statements
class AlphaCollection extends FormCollection
{
public function __construct()
{
parent::setWrapper('<div class="alpha-form-collection">%2$s%1$s%3$s</div>');
}
/**
* @param \Zend\Form\ElementInterface $element
* @param null $labelPosition
* @return string
*/
public function render(ElementInterface $element, $labelPosition = null)
{
$markup = parent::render($element, $labelPosition);
$classes = 'input-field col s12 alpha-fieldset';
if($element instanceof Collection)
{
$classes .= ' alpha-fieldset-collection';
}
$prepend = '<div class="' . $classes . '">';
$append = '</div>';
return $prepend . $markup . $append;
}
}
So in essence, we're not so much creating our own ViewHelpers so much as changing the ones provided by Zend Framework 3. Because we're just "updating" existing ones, we don't have to create new Factories (no additional requirements).
Zend Framework has registered ViewHelpers with invokable names (so you can do $this->formRow(...)
or $this->formSelect(...)
. We've just hijacked their configuration and replaced the class we need with our own. That way, when we have a form generated completely (<?= $this->form($form) ?>
), ZF does all the work for us.
Implementation in a .phtml
:
<!-- Internally uses the invokables we've modified, so this is all we need to do :) -->
<?= $this->form($form) ?>
To make the configuration a bit more future proof, I think you can replace the string invokable with a FQCN nowadays (have not tested this (yet))
Custom Form Builder in Rails 5.1
Shouldn't it be
<%= f.radio_button(:admin, "child") %>
instead of
<%= f.div_radio_button(:admin, "child") %>
UPDATE
Sorry I hurriedly answered this yesterday. Looking at it again you seem to be missing the builder option of the form so you are still using the ActionView::Helpers::FormBuilder instead of your custom form builder class.
<%= form_for @person, :builder => MyFormBuilder do |f| %>
I am a child: <%= f.div_radio_button(:admin, "child") %>
I am an adult: <%= f.div_radio_button(:admin, "adult") %>
<% end -%>
The snippet above is from the docs
So what you need is probably this
<%= form_with model: @user, url: users_path, method: :post, builder: CustomFormBuilder do |f| %>
...
<%= f.div_radio_button(:admin, "child") %>
<% end %>
Related Topics
How to Install Ruby 2 on Ubuntu Without Rvm
Passing Multiple Error Classes to Ruby's Rescue Clause in a Dry Fashion
Comparing Two Arrays Ignoring Element Order in Ruby
Can Rails Migrations Be Used to Convert Data
Getting Rid of Ruby Gems That Won't Die
Rails: How to Use Dependent: :Destroy in Rails
Ruby - Lexical Scope VS Inheritance
What's the Difference Between Design Patterns and Design Principles
How to Randomly Sort (Scramble) an Array in Ruby
Devise Logged in Root Route Rails 3
Adding an Action to an Existing Controller (Ruby on Rails)
Setting Up Private Github Access with Aws Elastic Beanstalk and Ruby Container
Checking If a Variable Is an Integer
Implicit Return Values in Ruby
Any Success with Sinatra Working Together with Eventmachine Websockets