Show Pages Displaying Individual Model Objects
In this demonstration, I will show how to create a “show” pages for displaying individual model objects retrieved from the database.
General Steps
In general, the steps for creating show pages are as follows.
-
Step 1 Add a Show Route. This step specifies the URL resource-paths for the show pages and tells the web server which controller action to call to process requests for the show pages.
-
Step 2 Create a Model Controller (if Needed). If a controller class for the model objects to be displayed does not already exist, this step creates one.
-
Step 3 Create a Show Controller Action. This step adds an action to the controller that retrieves the model object to be displayed from the database and renders the HTML.ERB view template for the show page.
-
Step 4 Create a Show View Template. This step adds an show view template that prints the data from the retrieved model object to the view.
-
Step 5 Add Links to Show Pages. Links to show pages are particularly important, because show-page URLs contain ID numbers that are generated by the system and that a user wouldn’t typically know. This step generates links that enable users to access the various show pages.
Creating a Show Page for Individual To-Do Items
To demonstrate the steps for creating show pages, we will be creating show pages for a model class representing items in a to-do list. Figure 1 illustrates how a show page will look for a particular to-do item.
For this demo, we will continue to build upon the app from the index pages demo. Recall that the app contains a Todo
model class (depicted in Figure 2), a seeds script for populating the database with some sample Todo
objects, a TodosController
class, and a route, a controller action, and a view template for the index page.
In addition to implementing show pages for the Todo
objects, we will also add links from the index page to the show pages, as illustrated in Figure 3.
Step 1 Add a Show Route
To add a route for the show pages, we add a route declaration to the config/routes.rb
file, like this:
Rails.application.routes.draw do
get 'todos', to: 'todos#index', as: 'todos'
get 'todos/:id', to: 'todos#show', as: 'todo'
end
Note that, unlike the routes we’ve previously created, the resource path for the show route ('todos/:id'
) has a parameter (:id
) for specifying the ID of the model object to be displayed. For example, this route will match on requests for http://localhost:3000/todos/1, http://localhost:3000/todos/2, etc. When the controller action processes one of these requests, it will be able to retrieve the ID number from the URL, so it knows which model object to display.
Also, note that this route adheres to the following Rails resourceful-style naming conventions. The first part of the resource path (todos/
) and the controller name are both named like the model database table—that is, the model name in snake_case
and pluralized (i.e., todos
). Don’t forget that the name for the controller specified in the route (todos
) is a shorthand for the actual controller class name (TodosController
). The name of the controller action method is show
. The path/URL helper prefix is the snake_case
version of the model class name (i.e., todo
). Note that the path/URL helper prefix for the show route is singular (because show corresponds to a single model object), unlike the index route, which is plural (because index corresponds to multiple model objects).
Test It!
To confirm that we made this change correctly, we run the following command in the terminal:
rails routes -E
The command should output a long list of routes, but the very first one is the only one we care about. If we coded our route correctly, it should look like this:
--[ Route 2 ]-------------------------------------------------------------------------------------------------------------------
Prefix | todo
Verb | GET
URI | /todos/:id(.:format)
Controller#Action | todos#show
If we made an error, something different will be outputted. For example, if we made a syntax error, it would instead produce an error message.
Step 2 Create a Model Controller (if Needed)
Because we previously generated the TodosController
, we skip this step.
Step 3 Create a Show Controller Action
To create the controller action for the show pages, we perform two substeps: first, we create the controller action code for rendering the view, and then, we add the code for retrieving the model object that will be displayed in the view.
Substep
Create a show controller action that renders the appropriate view. To complete this substep, we define an show
method in the TodosController
class (in app/controllers/todos_controller.rb
), like this:
class TodosController < ApplicationController
def index
@todos = Todo.order(:due_date)
render :index
end
def show
render :show
end
end
Note that, so far, the show
controller action only renders the view template app/views/todos/show.html.erb
(as specified by the :show
symbol).
Substep
Make the controller action retrieve the model object to be displayed in the view. To complete this substep, we add a call to the Todo.find
model class method to the show
method of the TodosController
class, like this:
def show
@todo = Todo.find(params[:id])
render :show
end
The find
model class method is used to retrieve a model object from the database by id
. The above call to Todo.find
returns a Todo
object (i.e., the one retrieved from the database), and the @todo
instance variable is assigned a reference to that object. The use of an instance variable (indicated by the @
in @todo
) is important here, because instance variables are accessible within view templates rendered by the class, thus allowing data to be passed from the controller to the view. The params
method is provided by Rails as a way to access the all the data received in the HTTP request to be processed by the controller action. Although params
is technically a method, it can be used like a hash. The above call to params[:id]
returns the ID embedded in the resource path of the HTTP request (recall the 'todos/:id'
part of the show route). The ID is, in turn, passed to the find
call, effectively telling the call which Todo
object to retrieve from the database.
Test It!
To at least partially confirm that we made this change correctly, we run the web app (as per the steps in the running apps demo), and we open the URL http://localhost:3000/todos/4 in our web browser. An error page should be displayed with the message “Template is missing”, because we have not yet created a view template for the show page. If a different error message appears (e.g., a syntax error), then we made some other mistake, which we would want to fix before proceeding to the next step.
Step 4 Create a Show View Template
To print the retrieved Todo
object on a page (as shown in Figure 1), we first create a view template containing an HTML skeleton for the page, and then, we add logic that fills in the page with data for the object.
Substep
Rough in the HTML skeleton. To complete this substep, we create a new view template file in the folder app/views/todos/
named show.html.erb
, and we add HTML elements to the file, like this:
<h1>TITLE_GOES_HERE</h1>
<p>
Due: DUE_DATE_GOES_HERE
</p>
<p>
DESCRIPTION_GOES_HERE
</p>
Note that, for now, the page has only “X_GOES_HERE” placeholder text for the to-do item’s data.
Substep
Fill in the page with the data for the retrieved model object. To fill in the page the data of the Todo
object, we replace the “X_GOES_HERE” placeholder values with embedded Ruby calls to the appropriate Todo
attribute accessor methods, like this:
<h1><%= @todo.title %></h1>
<p>
Due: <%= @todo.due_date.strftime("%m/%d/%Y") %>
</p>
<p>
<%= @todo.description %>
</p>
In the above view code, the @todo
variable comes from the show
method above and references the retrieved Todo
object. The calls to title
, description
, and due_date
each return the value of the corresponding attribute for the object. The list of Todo
class attributes can be found in the comments at the beginning of the model class file, app/models/todo.rb
. Surrounding each call to an attribute accessor method in the embedded Ruby tag <%= … %>
(note the =
) causes the value returned by the method to be written to the generated HTML. Note that the Date
method strftime
method was used to format the due_date
attribute. Here, we passed strftime
the argument "%m/%d/%Y"
, which will format the date as MM/DD/YYYY (e.g., 02/14/2021).
Test It!
To verify that we performed all parts of this step correctly, we run the web app (as per the steps in the running apps demo), and we open the URL http://localhost:3000/todos/4 in our web browser. The webpage displayed should look exactly like Figure 1.
Step 5 Add Links to Show Pages
To add links from the index page to the various show pages, we add code to the index view template (`app/views/todos/index.html.erb) such that the title of each model object is wrapped in a link to the object’s show page, like this:
<h1>To-Do Items</h1>
<table class="table table-hover">
<thead class="thead-dark">
<tr>
<th>Title</th>
<th>Due Date</th>
</tr>
</thead>
<tbody>
<% @todos.each do |todo| %>
<tr>
<td><%= link_to todo.title, todo_path(todo) %></td>
<td><%= todo.due_date.strftime("%m/%d/%Y") %></td>
</tr>
<% end %>
</tbody>
</table>
Note that we follow the general steps from the internal links demo to add the link; however, there are a couple of new things to note. For one, we get the link text from a method call (todo.title
) rather than a string literal. For another, the show route has a singular path/URL helper prefix ('todo'
) and a parameterized resource path ('todos/:id'
). As a consequence, the show page’s path/URL helper methods must also take a parameter to fill in the ID value. Thus, in the above code, we use the path helper call, todo_path(todo)
, which passes the current Todo
object in as an argument. The todo_path
method can tell that its todo
argument references a model object, and knows how to get the ID from that object.
Test It!
To verify that we completed this step correctly, we run the web app (as per the steps in the running apps demo), and we open the URL http://localhost:3000/todos in our web browser. The webpage displayed should look exactly like Figure 3. Clicking any link on the page should open the corresponding todo item’s show page.
Conclusion
Following the above steps, we have now added show pages to the to-do list app.