- Describe how templates & views work together
- Use partials for static content and rendering dynamic content
In Rails, the logic for the rendering a view is quite straightforward. Given that every route in Rails will execute a method inside a controller, when the method is executed, Rails will look for:
- A folder inside
views
corresponding to the controller's name (folderposts
forPostsController
). - A file with the method's name and
.html.erb
.
For example , if we call http://localhost:3000/posts
, Rails will execute the method index
in the controller posts
and then look for a template located in app/views/posts/index.html.erb
This works when the method always renders the same template.
In some cases though, you may want to render a template with a different name than the current method. Let's take a look at this action:
def create
@post = Post.new(post_params)
respond_to do |format|
if @post.save
format.html { redirect_to @post, notice: 'Post was successfully created.' }
format.json { render :show, status: :created, location: @post }
else
format.html { render :new }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
Based on the result of @post.save
, the method will execute either the code in the if
or in the else
. The code format.html
or format.json
means that Rails will understand the format asked by the user, html or JSON. We will look at JSON later in the course, so for the moment, we will only look at the lines starting with format.html
.
In the if
case , we can see that in the code executed in the block redirect_to @post, notice: 'Post was successfully created.'
This code will redirect the request to the show method. redirect_to
the @post
object in rails means "go to the method to show only this object".
In the else
case, the code executed is render :new
- this means, Rails will show the template app/views/post/new.html.erb
. This template uses an instance variable @post
. In this case, it will use the instance variable defined at the start of the create method.
To sum it up, Rails will, by default, render the template that has the name of the current method in the controller, unless there is a render
statement in the method that tells Rails to use a different template.
There are different syntaxes for render, and they all do the same action described above but the rule of thumb is to use the simplest one that makes sense for the code you are writing.
render :edit
render action: :edit
render "edit"
render "edit.html.erb"
render action: "edit"
render action: "edit.html.erb"
render "books/edit"
render "books/edit.html.erb"
render template: "books/edit"
render template: "books/edit.html.erb"
render "/path/to/rails/app/views/books/edit"
render "/path/to/rails/app/views/books/edit.html.erb"
render file: "/path/to/rails/app/views/books/edit"
render file: "/path/to/rails/app/views/books/edit.html.erb"
By default, Rails will render the layout application.html.erb
but sometimes, you want to render a template in a different layout.
For instance, to create a layout called sidebar.html.erb
touch app/views/layouts/sidebar.html.erb
Take this template and add it into the sidebar.html.erb
:
<!DOCTYPE html>
<html>
<head>
<title>Sidebar Template</title>
</head>
<body>
<header>
<h1>My Website</h1>
<ul>
<li>Menu 1</li>
<li>Menu 2</li>
<li>Menu 3</li>
<li>Menu 4</li>
</ul>
</header>
<main>
<%= yield %>
</main>
<footer>
<ul>
<li>About us</li>
<li>Team</li>
<li>Terms and conditions</li>
</ul>
</footer>
</body>
</html>
This will help us to differentiate the layouts.
In the controller method index
, add this to the end of the method:
render layout: "sidebar"
This line will just tell Rails to use the same logic of template rendering, but instead of using the default application.html.erb
, it will render the template inside sidebar.html.erb
. You can also specify the layout for a whole controller by specifying at the top of you Controller class, before your actions.
layout: "sidebar"
A best practice is to always keep every template as small as possible. A rule of thumb would be to keep shorter than 50 lines, for the sake of readability.
So for example, if your website has a layout with a top menu, the content and then the footer, this should all be in the layout, as it is rendered on every page. But if all this html is in one file, the layout will end up being 200+ lines of code.
When this happens, you should think about splitting the template into partials. Partials are just chunks of a template that will be rendered into another one, like children of the layout.
This layout in sidebar.html.erb
will soon be too long and unreadable, thus we need to split into different parts:
Create a new file:
mkdir app/views/application
touch app/views/application/_header.html.erb
And inside move the following from sidebar.html.erb
:
<header>
<h1>My Website</h1>
<ul>
<li>Menu 1</li>
<li>Menu 2</li>
<li>Menu 3</li>
<li>Menu 4</li>
</ul>
</header>
Now let's create another file:
touch app/views/application/_footer.html.erb
And inside move the following from sidebar.html.erb
:
<footer>
<ul>
<li>About us</li>
<li>Team</li>
<li>Terms and conditions</li>
</ul>
</footer>
Now the main sidebar.html.erb
file is back to a normal size, we just need to include the partials and the final result will then be the same than before.
Note: Every partial needs to have an underscore as the first character - this way Rails knows that it is not a proper template but only a partial that will be included in a template.
Let's now call the partials in the layout:
<!DOCTYPE html>
<html>
<head>
<title>Sidebar Template</title>
</head>
<body>
<%= render "header" %>
<main>
<%= yield %>
</main>
<%= render "footer" %>
</body>
</html>
Rails will automatically look in the folder app/views/application/
for a file that is called by the name given to the method render
with an underscore prefix.