Lesson Weekend

In the last few lessons, we added routes and a controller to our record store application. We have full CRUD functionality for albums but our application still lacks a user interface. Let's complete this functionality by adding views to our application.

Rails uses embedded Ruby (.erb) files just like Sinatra does. In fact, many of our views will look very similar to the views we created with Sinatra applications in the last two course sections. There are really just a few significant differences to watch out for:

  • The file structure and naming patterns for views in Rails follows a specific convention.
  • Forms will look different because Rails uses Action View form helpers to build forms. These helpers are in place to make form building easier but they also abstract away some of the code we are used to seeing.

Naming Conventions

Let's start by looking at the naming convention for views. All of our views will be stored in the app/views subdirectory. Each class will also have a directory for views as well. Go ahead and create the following subdirectory album and its files:


A few things to note here:

  • We call our subdirectory albums. We should always use the pluralized, lower-cased name of the class for naming so Rails can find the right directory. Rails takes care of a lot of things under the hood for us but relies on proper naming to find things.
  • Each of our views has a name corresponding with the controller action that renders it. This makes it much easier to navigate around our code, especially as our applications get larger.
  • We include html in the extension name like this: .html.erb. This specifies that this is an html file that includes embedded Ruby. This is because .erb files can be used for other formats beside html. This is different from Sinatra convention, which is fine with just the .erb extension.

Our index view will look very familiar because it uses some of the same code we used with our Sinatra views. However, there is one big new addition which will cover in a moment.


<% if @albums.any? %>
    <% @albums.each do |album| %>
      <li><%= link_to album.name, album_path(album) %></li>
    <% end %>
<% else %>
  <p>There are no albums yet.</p>
<% end %>

<p><%= link_to "Create new album", new_album_path %></p>

Just like with our Sinatra views, we combine a loop and a conditional to either show a list of albums or let the user know that there are no albums yet. Our instance variables get their values from the controller action just as a route in app.rb passed along values to a view's instance variable in Sinatra.

The main difference here is that we are using link_to, which is a helper method Rails provides. This is just an easier way of creating links in our application. The format of a link_to looks like this:

<%= link_to <visible part of link>, <route prefix plus path or url> %>

A couple of things to note:

  • link_to helpers always go inside embedded Ruby tags. Make sure to include the = sign so the actual link is visible.
  • The first argument in a link_to helper method is the visible part of the link. This could be a string or it could be something dynamic like album.name.
  • The second argument is the route path or URL. If we are linking to a route inside our application, this should be the route prefix plus _path. For example, in our link to creating a new album above, we use the new_album_path. We can always verify the name of the route prefix by checking rake routes. If we are linking to a URL that's not part of our application, that can go here instead.

In our first link_to helper, the path is album_path(album). album_path must take an argument because otherwise our application won't be able to route to a specific album. Note that we don't have to pass in album.id. We can just pass in album and Rails will take care of the rest for us. Any route path that needs an id must specify an argument. We'll see a few more examples of this in our next view.

Show View and Destroying an Album

Let's work on the view for showing an individual album next. In the process, we'll also add link_to helpers for linking to the edit page and for destroying an album.

<h1><%= @album.name %></h1>
<h3><%= @album.genre %></h3>

<p><%= link_to "Edit", edit_album_path(@album) %></p>
<p><%= link_to "Delete", album_path(@album),
                         :data => {:confirm => "You sure?",
                                   :method => "delete"} %></p>

<p><%= link_to "Return to albums", albums_path %></p>

Note that both the edit_album_path and the album_path for deleting take @album as an argument. Once again, this is because Rails need to know which album we want to edit or destroy.

The link_to helper for destroying an album looks a little different. Here, we use link_to's optional third argument, which is where we pass in additional html options.

  • First, we can use :data to add custom data attributes. Note that :data is a key-value pair with a hash as the value.
  • :confirm uses a JavaScript confirm() message to verify that the user wants to destroy an album. There are multiple different ways to use JavaScript with Rails and this is a simple implementation.
  • :method allows us to specify the method we want this link_to helper to use. If we look at rake routes, we'll see that GET, PATCH, PUT (essentially the same as PATCH) and DELETE all use the album_path. For that reason, we need to specify "delete" as the method.
  • When our application sees :method within a :data hash, it will "fake" a different method than GET and add rel="nofollow", which tells search engines and other bots that might be crawling an application's page not to click the link. We don't want them going around deleting all of our data.

New View and Rails Form Helpers

Now let's make a view for adding an album to the database. We'll use a Rails Form Helper to build the form:

<h1>New album</h1>

<%= form_for @album do |f| %>
  <%= f.label :name %>
  <%= f.text_field :name %>
  <%= f.label :genre %>
  <%= f.text_field :genre %>
  <%= f.submit  %>
<% end %>

Rails provides the form_for helper method which makes creating forms for database-backed objects quick and easy. form_for takes an object as an argument and builds a form around it. This is why we need the line @album = Album.new in the new action of our AlbumsController.

We use f.label to provide a label and f.text_field to provide a field for text input. The arguments that are passed in are properties of an Album object. If we tried to pass in :random_property, our application would throw an exception. The form_for helper is specifically looking for symbols that have the same name as one of the object's properties. Finally, we need to have an f.submit field as well.

Rails will generate the necessary HTML for us and will even add a hidden field that protects against cross-site request forgery (CSRF) attacks.

The benefits of a form helper may not be as apparent in a simple form like this, but it can be very useful for more complex forms, checkboxes, radio buttons, and so on.

Edit View

Our edit view will have the exact same form as our new view:

<h1>Edit album</h1>

<%= form_for @album do |f| %>
  <%= f.label :name %>
  <%= f.text_field :name %>
  <%= f.label :genre %>
  <%= f.text_field :genre %>
  <%= f.submit  %>
<% end %>

It's not very DRY to use the same code in multiple views. In a future lesson, we'll learn how to use partials to reduce redundancy in our code.

In this lesson, we covered the basics of views in Rails. Some of the code is very similar to .erb views we built with Sinatra applications in the last few course sections. We also covered form helpers and link helpers, two features that Rails provides that make it easier to build our applications.

We can now go to the root directory of our project, type in rails s, and then navigate to localhost:3000 in our browser to see our application's new CRUD functionality.

In the next lesson, we'll do the same for building out CRUD functionality for songs.

Lesson 11 of 34
Last updated July 14, 2022