Understanding and extending Rails form helpers

Oct 6, 2015 - 6 min read
Understanding and extending Rails form helpers
Share

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.

12 Project Ideas
Cezar Halmagean
Software development consultant with over a decade of experience in helping growing companies scale large Ruby on Rails applications. Has written about the process of building Ruby on Rails applications in RubyWeekly, SemaphoreCI, and Foundr.