DEV Community

Cover image for Creating Forms in Ruby on Rails with Simple Form
Thomas Riboulet for AppSignal

Posted on • Originally published at blog.appsignal.com

Creating Forms in Ruby on Rails with Simple Form

Ruby on Rails has changed how we build web applications. Early on, the framework came with some great features to help you get started and build robust applications.

However, it can still be tricky to build and handle forms. Simple Form is a great option. Let's examine what Simple Form is, why we might need it, and some real use cases.

Forms and Ruby on Rails

Ruby on Rails really simplifies building applications. Yet, it also requires your constant attention to keep your codebase streamlined and coherent. One strategy is to avoid writing code by using abstractions, for example. Code that isn't in your actual application is code you don't have to manage. That's why we like abstractions so much, right? Simple Form is just such an abstraction that simplifies building forms for web pages.

Forms are generally complex to build, even using Ruby on Rails. Each field requires attention to define the right type and attach it to the correct attribute. Simple Form eases that pain, as you don't need to find the exact type required for each field.

Let's take a form that gets a user's details, with name, username, and email fields. Consider that we have the User model with similar related attributes. With Simple Form, the form that fills in attributes looks like the following:

<%= simple_form_for @user do |f| %>
  <%= f.input :name, required: false %>
  <%= f.input :username %>
  <%= f.input :email %>
  <%= f.button :submit %>
<% end %>
Enter fullscreen mode Exit fullscreen mode

Note that we are not specifying the types of each field. Simple Form will pick the correct input type for each field based on the column's type.

This saves us time and keeps the code readable and easier to maintain.

Installing Simple Form

To install Simple Form in your project, add the simple_form gem to the Gemfile. Once you have run bundle install, you can use the included generator to set it up correctly for your application.

rails generate simple_form:install
Enter fullscreen mode Exit fullscreen mode

A note on Bootstrap and Zurb Foundation: Both Bootstrap and Zurb Foundation 5 are supported within Simple Form. So, if you are using one of them, you can use the --bootstrap or --foundation parameter with the generator to include additional configurations during the installation. In the case of Bootstrap, this would look like the following:

rails generate simple_form:install --bootstrap
Enter fullscreen mode Exit fullscreen mode

Basic Usage of Simple Form

As we pointed out earlier, Simple Form will generate a complete form mainly based on the data related to your objects. Yet, it's also important to understand that Simple Form will not only generate the appropriate field for each attribute but also provide labels and display error hints above the input fields themselves!

Let's consider a fresh new Ruby on Rails 7.x application. We'll generate a User model and build a form for it using Simple Form.

rails g model User username:string password:string email:string remember_me:boolean
# and follow with running the migration of course
rails db:migrate
Enter fullscreen mode Exit fullscreen mode

Now we can generate a controller with the new, create, and index actions. The aim is to have something we can play with in the form.

rails g controller users
Enter fullscreen mode Exit fullscreen mode
# app/users_controller.rb
class UsersController < ApplicationController
  def new
    render :new, locals: { user: User.new }
  end

  def create
    user = User.create(user_params)
    redirect_to users_path
  end

  def index
    render :index, locals: { users: User.all }
  end

  private

  def user_params
    params.require(:user).permit(:username, :password, :email)
  end
end
Enter fullscreen mode Exit fullscreen mode
# config/routes.rb
Rails.application.routes.draw do
  resources :users, only: [:new, :create, :index]
end
Enter fullscreen mode Exit fullscreen mode

These actions require relevant views. Here is a form that handles a user signing up or being added:

<!-- app/views/users/new.html.erb -->
<%= simple_form_for user do |f| %>
  <%= f.input :username, label: 'Your username please', error: 'Username is mandatory, please specify one' %>
  <%= f.input :password, hint: 'No special characters.' %>
  <%= f.input :email, placeholder: 'user@domain.com' %>
  <%= f.input :remember_me, inline_label: 'Yes, remember me' %>
  <%= f.button :submit %>
<% end %>
Enter fullscreen mode Exit fullscreen mode

In this case, we specify a custom label and error for the username input. If no such customization is passed, it will do its best to guess what the label needs to be based on the attribute's name.

We have set a custom hint for the password field and a custom placeholder for the email field. Notice, also, the ability to specify an "inline label" instead of one located above the field (the usual default in forms).

It's also possible to disable labels, hints, and errors by passing the false value for those attributes: <%= f.input :password_confirmation, label: false %>.

We need to complement this with one more view to make it usable.

<!-- app/views/users/index.html.erb -->
<ul>
<% users.each do |user| %>
  <li><%= user.username %> (<%= user.email %>)</li>
<% end %>
</ul>
<p>
<%= link_to 'New User', new_user_path %>
</p>
Enter fullscreen mode Exit fullscreen mode

You can then start the application using rails s. Head to localhost:3000/users/new to see and use the form.
Let's focus on how the form differs from a classic Ruby on Rails form.

A note on validations: Simple Form can work with mandatory fields out of the box. For example, if we were to add the following presence validation in our User model:

class User < ApplicationRecord
  validates :username, presence:true

  # more code
end
Enter fullscreen mode Exit fullscreen mode

This will be used by Simple Form to add a little '*' next to the username field, specifying that it's mandatory. This doesn't manage the errors automatically, so you still have to handle that in the controller action. Return to the appropriate field that has errors and act on them.

About Column Types and Form Fields

As mentioned earlier, we haven't specified a type for each field. The Simple Form README has a complete list of all available input types and the defaults for each column type. Here are the most used ones:

Column Type Generated HTML element Comment
string input[type=text]
boolean input[type=checkbox]
(passwords) string input[type=password] Any column with a name containing 'password'
text input[type=textarea]
integer, float input[type=number]
datetime datetime select
time time select
country select

Booleans

As we can see in the above table, boolean attributes will be represented, by default, as checkboxes. In many cases, that's what we'll want. But if not, there is a way to customize this in Simple Form with the as attribute, which allows you to specify if you will show radio buttons or a dropdown instead.

Here is how to specify radio buttons instead:

<%= f.input :remember_me, input_html: { value: '1' }, as: :radio_buttons %>
Enter fullscreen mode Exit fullscreen mode

This will generate the following HTML:

<div class="input radio_buttons optional user_remember_me">
  <label class="radio_buttons optional">Remember me</label>
  <input type="hidden" name="user[remember_me]" value="" autocomplete="off" />
  <span class="radio">
    <label for="user_remember_me_true">
      <input
        value="true"
        class="radio_buttons optional"
        type="radio"
        name="user[remember_me]"
        id="user_remember_me_true"
      />Yes
    </label>
  </span>
  <span class="radio">
    <label for="user_remember_me_false">
      <input
        value="false"
        class="radio_buttons optional"
        readonly="readonly"
        type="radio"
        name="user[remember_me]"
        id="user_remember_me_false"
      />No
    </label>
  </span>
</div>
Enter fullscreen mode Exit fullscreen mode

It's impressive that such a small piece of Ruby code gets you such a complete piece of HTML!

HTML

From the previous example, we can see how Simple Form generates a lot of HTML for each field. That includes the HTML for the input field and a wrapper div around the label and input field. We can customize those by specifying a custom class and id using the input_html and wrapper_html attributes. Here is an example.

<%= simple_form_for @user do |f| %>
  <%= f.input :username, wrapper_html: { class: 'username' }, input_html { id: 'username' } %>
<% end %>
Enter fullscreen mode Exit fullscreen mode

That's also a way to set attributes for the related HTML, such as maxlength or value. Let's take the password field from our user form. We can limit the size and length of the field with the maxlength attribute.

<%= f.input :password, input_html: { maxlength: 20 } %>
Enter fullscreen mode Exit fullscreen mode

Custom Inputs and Additional Options

Simple Form is a Ruby library that prepares HTML nodes for you. It comes with a whole set of fields, but you can also add your own. To do so, you can create classes inheriting from Simple Form's classes. Let's consider defining a custom social network input field with a little '@' prefix in front of the actual field.

# app/inputs/social_handle_input.rb
class SocialHandleInput < SimpleForm::Inputs::Base
  def input(wrapper_options)
    merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)

    "@ #{@builder.text_field(attribute_name, merged_input_options)}".html_safe
  end
end
Enter fullscreen mode Exit fullscreen mode

Now we can use it in the following way:

<%= f.input :network_handle, as: :social_handle %>
Enter fullscreen mode Exit fullscreen mode

Note that, if your User model doesn't have a network_handle attribute, you must add it through a migration or cheat with an attr_accessor in the model.

i18n Support

As we've mentioned, building forms is often a pain point. But things get even more painful when it comes to websites and applications that support multiple languages. Simple Form, thankfully, follows Ruby on Rails standards. You can use a simple_form key in the local files to define translations for all labels, hints, placeholders, prompts, etc.

Here is a little example:

en:
  simple_form:
    labels:
      user:
        username: 'User name'
        password: 'Password'
    hints:
      user:
        username: 'User name to sign in.'
        password: 'No special characters, please.'
    placeholders:
      user:
        username: 'Your username'
        password: '****'
    include_blanks:
      user:
        age: 'Rather not say'
    prompts:
      user:
        role: 'Select your role'
Enter fullscreen mode Exit fullscreen mode

Value Objects

Sometimes, we might use custom, non-ActiveRecord classes to instantiate the main object a form relies upon. This can be done to compose data from multiple models into a synthetic one for either business logic reasons or to gather several attributes into a single object.

Whatever the reason, you can still rely on Simple Form to build forms for that kind of object. To do so, the object class needs to implement, in the best case, three methods: to_model, to_key, and persisted?.

The to_model method will point to the object itself:

def to_model
  self
end
Enter fullscreen mode Exit fullscreen mode

The to_key allows us to point to the identifier attribute for the object — usually, that means an id attribute named:

def to_key
  id
end
Enter fullscreen mode Exit fullscreen mode

Finally, the persisted? method is there to tell Simple Form if the object is directly persisted or not.

def persisted?
  false
end
Enter fullscreen mode Exit fullscreen mode

If that method isn't present, the f.submit helper isn't usable.

There is a faster way, though — including the ActiveModel::Model module in the class:

# app/models/account.rb
class Account
  include ActiveModel::Model

  attr_accessor :id, :company_name
end
Enter fullscreen mode Exit fullscreen mode

And let's use it through the User model.

# app/models/user.rb
class User < ApplicationRecord
  attr_accessor :network_handle

  def account
    @account ||= Account.new(id: 1, company_name: "Acme Inc.")
  end

  def company_name
    account.company_name
  end
end
Enter fullscreen mode Exit fullscreen mode

We can then add a field for the company name within the user form:

<%= f.input :company_name %>
Enter fullscreen mode Exit fullscreen mode

Of course, this attribute will not get saved in a table. However, the same concept can be applied to compose a value object with attributes pulled from several models.

Wrapping Up

In this post, we've seen that Simple Form is easy to install and integrate into your Ruby on Rails application. Simple Form not only takes care of the details of each field in your form, but also generates complex and complete HTML so your form can be styled and shaped with ease.

I encourage you to dive into Simple Form's documentation to see how powerful it can be.

Happy coding!

P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, subscribe to our Ruby Magic newsletter and never miss a single post!

Top comments (0)