Skip to content

Ruby on Rails - Views

Views in Rails are the ‘V’ in MVC. They are responsible for generating the user interface, typically HTML. Rails views are most commonly written using ERB (Embedded Ruby), which allows you to embed Ruby code within HTML documents. Data is passed from controllers to views via instance variables.

When you generate a controller, Rails typically creates a corresponding subdirectory in app/views/. For example, if you have a BooksController, its views will reside in app/views/books/. Each public controller action usually has a corresponding view template file (e.g., index.html.erb for the index action).

Let’s create view files for the actions defined in our BooksController. As we build these, consider how the data flows from the controller and how Rails helpers simplify HTML generation.

Creating View for index Action (Listing Books)

Section titled “Creating View for index Action (Listing Books)”

Create a file named index.html.erb in app/views/books/. This view will display a list of all books. If no books exist, it will show a message.

Add the following ERB code to app/views/books/index.html.erb:

<h1>Available Books</h1>
<% if @books.empty? %>
<p>There are no books currently in the system.</p>
<% else %>
<p>These are the current books in our system:</p>
<ul id="books">
<% @books.each do |book| %>
<li id="book_<%= book.id %>">
<%= link_to book.title, book_path(book) %>
<%= link_to 'Edit', edit_book_path(book) %>
<%= link_to 'Delete', book_path(book), data: { turbo_method: :delete, turbo_confirm: 'Are you sure you want to delete this item?' } %>
</li>
<% end %>
</ul>
<% end %>
<p><%= link_to "Add new Book", new_book_path %></p>

Explanation:

  • @books.empty? checks if the @books instance variable (set in the controller’s index action) is empty.
  • @books.each do |book| ... end iterates over each book.
  • link_to book.title, book_path(book) creates a link to the book’s show page. book_path(book) is a route helper that generates the URL for books#show for the given book object.
  • edit_book_path(book) links to the edit page for the book.
  • link_to 'Delete', book_path(book), data: { turbo_method: :delete, turbo_confirm: '...' } creates a delete link. Modern Rails uses Turbo for such actions, requiring turbo_method: :delete. The turbo_confirm provides a JavaScript confirmation dialog.
  • new_book_path links to the page for creating a new book.

If you navigate to /books in your browser and no books have been added, you’ll see the message “There are no books currently in the system.” and a link to add a new book.

Creating View for new Action (New Book Form)

Section titled “Creating View for new Action (New Book Form)”

To add books, we need a form. Create new.html.erb in app/views/books/.

Add the following code to app/views/books/new.html.erb:

<h1>Add New Book</h1>
<%= form_with(model: @book, local: true) do |form| %>
<% if @book.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@book.errors.count, "error") %> prohibited this book from being saved:</h2>
<ul>
<% @book.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= form.label :title %><br>
<%= form.text_field :title %>
</p>
<p>
<%= form.label :price %><br>
<%= form.number_field :price, step: 0.01 %>
</p>
<p>
<%= form.label :subject_id %><br>
<%= form.collection_select :subject_id, @subjects, :id, :name, prompt: 'Select a Subject' %>
</p>
<p>
<%= form.label :description %><br>
<%= form.text_area :description %>
</p>
<p>
<%= form.submit 'Create Book' %>
</p>
<% end %>
<%= link_to 'Back to List', books_path %>

Explanation:

  • form_with(model: @book, local: true) do |form| ... end is the modern Rails form builder. model: @book tells it to build a form for the @book object (instantiated in the controller’s new action). local: true ensures a standard HTML form submission rather than an AJAX one by default.
  • The error display block shows validation errors if saving fails.
  • form.label, form.text_field, form.number_field, form.collection_select, form.text_area, and form.submit are form helper methods that generate the corresponding HTML form elements.
  • collection_select is used for the ‘Subject’ dropdown, populated by @subjects (passed from the controller).
  • Navigating to /books/new will display this form.

After filling out the form and clicking “Create Book”, the data is submitted to the create action. If successful, you’ll be redirected to the book list, now showing the new book. If there are validation errors, the new page will re-render with error messages.

Creating View for show Action (Displaying a Book)

Section titled “Creating View for show Action (Displaying a Book)”

This view displays details for a specific book. Create show.html.erb in app/views/books/.

Add the following code:

<h1><%= @book.title %></h1>
<p>
**Price:** <%= number_to_currency(@book.price) %><br>
**Subject:** <%= @book.subject&.name || 'N/A' %><br>
**Created Date:** <%= @book.created_at.strftime("%B %d, %Y") %><br>
</p>
<p>
**Description:**<br>
<%= @book.description %>
</p>
<hr>
<%= link_to 'Edit this Book', edit_book_path(@book) %> |
<%= link_to 'Back to List', books_path %>

Explanation:

  • @book.title, @book.price, etc., display attributes of the book found by the controller’s show action.
  • number_to_currency is a Rails helper for formatting prices.
  • @book.subject&.name uses Ruby’s safe navigation operator (&.) to prevent an error if a book doesn’t have an associated subject. It then displays the subject’s name or ‘N/A’.
  • strftime formats the created_at timestamp.
  • Links are provided to edit the book or go back to the list.

Creating View for edit Action (Edit Book Form)

Section titled “Creating View for edit Action (Edit Book Form)”

This form is similar to the new form but pre-filled with the book’s existing data and submits to the update action. Create edit.html.erb in app/views/books/.

Add the following code:

<h1>Edit Book Details</h1>
<%= form_with(model: @book, local: true) do |form| %>
<% if @book.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@book.errors.count, "error") %> prohibited this book from being saved:</h2>
<ul>
<% @book.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= form.label :title %><br>
<%= form.text_field :title %>
</p>
<p>
<%= form.label :price %><br>
<%= form.number_field :price, step: 0.01 %>
</p>
<p>
<%= form.label :subject_id %><br>
<%= form.collection_select :subject_id, @subjects, :id, :name, prompt: 'Select a Subject' %>
</p>
<p>
<%= form.label :description %><br>
<%= form.text_area :description %>
</p>
<p>
<%= form.submit 'Update Book' %>
</p>
<% end %>
<%= link_to 'Show this Book', book_path(@book) %> |
<%= link_to 'Back to List', books_path %>

This form is almost identical to the new form. form_with(model: @book) automatically sets the form’s action to submit to PATCH /books/:id (the update action) and pre-fills fields because @book is an existing record.

The delete (or destroy) action typically doesn’t have its own view. After deleting a record, the controller usually redirects to the index page. The delete functionality is triggered by a link on the index page (or show page), as shown earlier.

Creating View for show_subjects Action (Example of Custom Action)

Section titled “Creating View for show_subjects Action (Example of Custom Action)”

If you have a custom action like show_subjects in your BooksController (perhaps to list books by a specific subject, though this logic often belongs in a SubjectsController), you’d create app/views/books/show_subjects.html.erb.

Assuming @subject and its associated @books are set in the controller:

<h1>Books in Subject: <%= @subject.name %></h1>
<% if @subject.books.empty? %>
<p>No books found for this subject.</p>
<% else %>
<ul>
<% @subject.books.each do |book| %>
<li><%= link_to book.title, book_path(book) %></li>
<% end %>
</ul>
<% end %>
<%= link_to 'Back to All Books', books_path %>

This demonstrates iterating through books associated with a particular subject. You would also need to set up a route and controller action for this. For example, in show.html.erb, you might link to this from the subject name: **Subject:** <%= link_to @book.subject&.name, subject_books_path(@book.subject) if @book.subject %> (assuming a route helper subject_books_path).

To list all subjects on the main book index page (app/views/books/index.html.erb), you could add (assuming @all_subjects is passed from the controller):

<h2>Browse by Subject</h2>
<ul id="subjects">
<% @all_subjects.each do |subject| %>
<li><%= link_to subject.name, books_path(subject_id: subject.id) %></li>
<%# Or link_to subject.name, subject_path(subject) if you have a subjects#show page %>
<% end %>
</ul>

This example shows one way to filter the book list by subject using query parameters. The controller’s index action would then need to handle this parameter.

With controllers handling logic and views presenting data, you have the core of a web application. The next chapter explores Layouts, which provide a consistent structure and appearance across multiple views.