Form helpers are probably one of the most used category of helpers and the least understood ones. You might find them especially confusing if you’re just beginning to learn Ruby on Rails.
We’re going to see how Rails form helpers make our lives easier and also how to customize their behavior.
What Are Rails Helpers
Helpers in rails are meant to clean up view code by allowing you to extract data processing logic from your markup code. By moving the logic out, you get an added benefit, you can test your “view logic” easier. At least that’s the most common use case you’ll see in Rails.
Here a basic example of what a helper method might look like:
def full_name(user)
content_tag :div, class: "user-fullname" do
user.first_name + " " + user.last_name
end
end
Another nice bonus is that you can have separate helpers (as in helper modules) for each of your controllers. So for example if you have a controller named WelcomeController
, that will automagically look for and include a helper module named WelcomeHelper
. So that means that you can separate your helper methods by context.
Also all helpers, like controllers, have a base module that they inherit from called ApplicationHelper
. This is where you would put the more generic helper methods that could be reused in other controllers/views. Be careful though as this one tends to become a dumping ground for any method that doesn’t seem to belong in it’s own designated helper module which turns ApplicationHelper
into a little monster in no time.
So in practice, you don’t really need to think about the mechanics, you just cut a bunch of logic out of the view and slap it into a helper method and you’re done. Easy as that.
I’ve written a more in depth article about Rails helpers if you are interested in reading more about Rails helpers.
Form Helpers in Rails
Form helpers are basically the same thing I’ve just described but they are focused on generating html forms. Form helpers allow you to create html forms by using a lot less code. Not only that but some form helpers also handle session authentication, field pre-population, validation and a bunch of other useful tasks.
Assuming we have a User model with a name field and an email field in our application, let’s take a look at a common form helper and it’s output.
<%= form_for User.new do |f| %>
<%= f.text_field :name %>
<%= f.text_field :email %>
<% end %>
Here is what the code above generates:
<form
class="new_user"
id="new_user"
action="/users"
accept-charset="UTF-8"
method="post"
>
<input name="utf8" type="hidden" value="✓" />
<input
type="hidden"
name="authenticity_token"
value="EYzW15dczYe7KJUcBTCJ+uFoernH12WYVvIksNnCPk7R/4hBYxnWEtr2+Camnak6kuFZDgxMHIQrIMUxPP9aTw=="
/>
<input type="text" name="user[name]" id="user_name" />
<input type="text" name="user[email]" id="user_email" />
</form>
As you can see there’s a lot going on in the html output, there are a bunch of lines we didn’t write. We only had to write 4 lines of view code and we got six back, and this is for a basic example.
You can see extra fields in there like the authenticity_token
and the utf8
encoding. There is also a dom id and a class name, plus the action path and charset.
Why Use Form Helpers at All
You can definitely use a regular html form instead of going through form helpers but you loose some of the convenience.
One example would be the authentication token that the helper generates for you, so you get security for free just by using a form helper instead of a plain html form.
Then there is the pre-population of form data. Say you have a user profile page where you want to allow the user to edit his profile. You can easily pre-populate the form with the user data just by providing the user object to the form helper.
How to Customise Form Helpers
By default, form helpers are using the default form builder which is responsible for generating the html that we’ve seen above for your form. So most of the magic in form helpers happens in this form builder class, which means you can extend it.
Let’s see an example of extending the default builder by tweaking the text_field
method to add some placeholder text for our user name.
class SiteController < ApplicationController
def index
@user = User.new
end
end
<%= form_for @user, builder: UserFormBuilder do |f| %>
<%= f.text_field :name %>
<% end %>
class UserFormBuilder < ActionView::Helpers::FormBuilder
def text_field(attribute, options={})
super(attribute, options.reverse_merge(placeholder: "Your name please"))
end
end
With that, we’ve added a default placeholder to the text field helper. This is probably not the best example but you get the idea. You can pretty much customize form helpers to do whatever you like.
How to Create Your Own Form Builder From Scratch
So we’ve seen how to extend the default form builder that Rails provides, but maybe you want a more customized builder. So let’s see how we can create a form builder from scratch.
We want to be able to create a form that uses a user_fields
helper and generates all the necessary fields for our user’s form. Something like the following:
<%= form_for @user, builder: CustomFormBuilder do |f| %>
<%= f.user_fields %>
<% end %>
By using form_for
, we get a few arguments passed into our custom builder’s initializer. The first argument is the record name, the second one is the record object (the user object in this case), the third argument is the template (the current view) and the fourth is an options hash.
So with that it should be pretty easy to create a form builder that can render a field for the user’s name and one for the user’s email.
class CustomFormBuilder
include ActionView::Helpers::TagHelper
include ActionView::Context
def initialize(_, user, template, options)
@user = user
@template = template
@options = options
end
def user_fields
user_name_field +
user_email_field
end
# This is required
def multipart?
end
private
def user_name_field(options={})
content_tag :div, class: "user-name" do
content_tag(:label, "Name: ", for: :name) +
content_tag(:input, nil, type: "text", value: @user.name)
end
end
def user_email_field(options={})
content_tag :div, class: "user-email" do
content_tag(:label, "Email: ", for: :email) +
content_tag(:input, nil, type: "text", value: @user.email)
end
end
end
Just to see the pre-population of the form, I’ve gone ahead and created a User
record in my db with the name “John Bon Jovi” and the email “me@example.com”.
And here is the final output.
<form
class="edit_user"
id="edit_user_1"
action="/user.1"
accept-charset="UTF-8"
method="post"
>
<input name="utf8" type="hidden" value="✓" />
<input type="hidden" name="_method" value="patch" />
<input
type="hidden"
name="authenticity_token"
value="0Hte2t1FDF3sx/AhW9sA2gxG35q1gDqIpoJNa4sEUyEQCABMKQAXyI0ZnRv4diAaf8/8LX4bQ5TbUKzqbjk3IA=="
/>
<div class="user-name">
<label for="name">Name: </label>
<input type="text" value="John Bon Jovi" />
</div>
<div class="user-email">
<label for="email">Email: </label>
<input type="text" value="me@example.com" />
</div>
</form>
Conclusion
Form builders are not that scary if you think of them as regular helpers with a focus on building forms. As you’ve seen in the examples above, it’s actually pretty easy to roll your own in just a few minutes.
Now, from my experience, it’s pretty hard to find a compelling reason to build your own form builder so that’s why I’d be curious to hear what you’ve built. So feel free to share your thoughts and code here.