How to Write Rails Forms

Codecabulary Home / Learn Rails / How to Write Rails Forms

When you write a form, it will usually correlate with a database table. A signup page will enter a new user in the users table and a playlist builder will add song/playlist associations to a joins table that correlates playlists with the songs they contain. Forms are thus a good way to begin thinking about Rails' RESTful architecture, which maps HTTP requests to CRUD queries. In particular, forms tend to map POST requests to CREATE commands (sending user information via HTTP triggers a create user SQL query in the database).

If you already know a bit about HTML forms, this notion will be familiar territory, but what makes Rails forms special is that they automatically generate the right HTML to perform the database entries for you. For instance:

<%= form_for(@user) do |f| %>
  <%= f.label :name %>
  <%= f.text_field :name %>
<% end %>

generates:

<form action="/users" class="new_user" id="new_user" method="post">
    <label for="user_name">Name</label>
    <input id="user_name" name="user[name]" type="text" />
</form>

The big thing to notice from an ActiveRecord perspective is that the name attribute for the text field is "user[name]." We know that the HTML name attribute is how the server will identify this particular form field, and it looks in this case an awful lot like single assignment using a hash, doesn't it? If we check the params that are sent from the form to the server (using binding.pry), we notice that we have a user hash as a param, which (assuming we had other fields on our form), looks something like this:

user = {
  name: "Brett Shollenberger", 
  email: "brett.shollenberger@example.com", 
  password: "foobar", 
  password_confirmation: "foobar"
}

So what's happened here? All the fields in our form have been grouped into a single user hash, with key/value pairs assigned based on the way Rails autogenerated the name attribute of our HTML input tags.

What's really exciting is that Rails has grouped all the attributes we need to create a valid user object into a single user hash, which is available to us in our controller. So when the form hits the create action, we can create a new User instance like so:

  def create
    @user = User.new(params[:user])
  end

Remembering that user is itself a key in the larger params hash. If we were to translate this out:

@user = User.new({
  name: "Brett Shollenberger",
  email: "brett.shollenberger@example.com"
  # ... etc
})

We'd see that Rails has made the process of submitting a form to create a new database entry as painless as possible. With this as background, let's take a look at the steps required to setup your forms in Rails.

  1. rails g model User username:string email:string.

Creating a user model is actually a bit more complex than this example. If we were doing this for reals, we'd also want to store an encrypted password in our database, which would require us to write a few more methods in our model class and include the bcrypt-ruby gem, but we don't need to go into those details for the purpose of this exercise. There are also a number of gems to use as validation systems (like Devise) that don't require you to understand the words "hash" and "salt."

  1. Write your validations, associations, rake db:migrate, etc.

Take care of the work involved in setting up your model layer. If you need some help, check out the Wiki's entries related to Active Record.

  1. rails g controller Users index new create show

We're only creating three actions on our controller to start--index (which will list all of our users), new (which will return a form to create a new user), and create (which takes that form data and actually goes about the business of creating a new user).

  1. Set up your routes

In your routes.rb file, add the line:

resources :users

This single line is mighty powerful: it maps seven requests to their appropriate controller actions, including the four that we're interested in: GET /users => "users#index", GET /users/new => "users#new", GET /user/:id => "users#show", and POST /users => "users#create."

  1. Build the new action

The new action's job is to return a form that will allow a visitor to create a new user. We'll later use the form_for helper method, which takes an ActiveRecord object and creates a form for it using its attributes, so we'll need to create a new ActiveRecord object:

  def new
    @user = User.new
  end
  1. Build the form

Although a controller's job is to return a response to the browser, we don't have to be explicit about the response in this case. If we don't setup a response for the new action, it will look in the views/users folder for a file named new.html.erb; if the file exists, it will return that. Since that's the conventional space for the new action, we should build our form there.

Here's where we'll reference the ActiveRecord object we created about in the form_for method; form_for takes a block and a variable, conventionally called "f" for form.

  <%= form_for(@user) do |f| %>
    <%= f.label :name %>
    <%= f.text_field :name %>

    <%= f.label :email %>
    <%= f.text_field :email %>

    <!--... -->
  <% end %>

Each HTML input type has a corresponding Rails input type, like radio button, password field, and text field.

  1. Build the create action

The new action will lead to the create action, since the form_for helper method will send a POST request to /users, as we saw above when we created the routes. So next we'll return to the controller to create the create action:

  def create
    @user = User.new(params[:user])
    if @user.save
      redirect_to @user, alert: "User created successfully."
    else
      redirect_to new_user_path, alert: "Error creating user."
    end
  end

We've seen this code earlier on, but remember: we're pulling out the hash user from the params hash, which contains all the attributes needed to create an ActiveRecord object. We try to save, and if we're successful, we show the user the new object; if not, we return to the form, including the alert "Error creating user."

  1. Build the show action

If you're following along, you'll have noticed that the redirect to @user has an error: Template not found. That's because we haven't created a template for the show action, whose job is to show a single user. Again, we'll start with the controller, where we'll need to build an array of all the users, and implicitly redirect to show.html.erb

  def show
    @users = User.find(params[:id])
  end
  1. Write show.html.erb

In show.html.erb, you'll be able to reference the user object's attributes like this:

  <p><%= @user.name %></p>
  <p><%= @user.email %></p>

You'll also likely want to offer a link back to the index page, so the client can see all the users:

  <%= link_to "Back", users_path %>

And in the future (though we won't go through it right now), you'll want to offer an edit option:

  <%= link_to "Edit", edit_user_path(@user)
  1. Write the index action

The index action will need to receive an array containing all the users, and it can implicitly call index.html.erb, like we've seen with show and new.

  def index
    @users = User.all
  end
  1. Write index.html.erb

On your index page, you can use a loop to display all of your users in a table:

  <table>
    <thead>
      <th>Name</th>
      <th>Email</th>
    </thead>
    <tbody>
      <% @users.each do |user| %>
        <tr>
          <td><%= user.name %></td>
          <td><%= user.email %></td>
        </tr>
      <% end %>
    </tbody>
  </table>