Rails provides several tools to reduce duplication in views, including layouts, partials, content_for, and helpers. Layouts allow separating header and footer code to be shared across pages. Partial files contain reusable chunks of view code that can be rendered multiple times. content_for allows passing content between files, such as dynamic page titles. The document discusses using these tools to DRY up duplicate form fields and model display code by extracting them into partials. When conventions like naming partials based on the model are followed, Rails can intelligently render the correct partial for a model or collection.
20. Repetitive HTML
Layouts help you to handle header and footer code
This is handy for HTML <head> … </head>
sections and common site design code
21. Repetitive HTML
Layouts help you to handle header and footer code
This is handy for HTML <head> … </head>
sections and common site design code
Rails will render a layout for each page, if available
22. Layout Selection
class ArticlesController <
ApplicationController
# ...
end
23. Layout Selection
class ArticlesController <
Each controller can ApplicationController
have it’s own layout # ...
end
24. Layout Selection
class ArticlesController <
Each controller can ApplicationController
have it’s own layout # ...
end
If a controller doesn’t,
Rails will check parent
controllers
25. Layout Selection
class ArticlesController <
Each controller can ApplicationController
have it’s own layout # ...
end
If a controller doesn’t,
Rails will check parent
controllers
application.html.erb
is the easiest way to
set a global layout
27. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>My Blog</title>
</head>
<body>
<%= yield %>
</body>
</html>
A Basic Layout
Just yield where you want to insert
the page content
28. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>My Blog</title>
</head>
<body>
<%= yield %>
</body>
</html>
A Basic Layout
Just yield where you want to insert
the page content
29. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>My Blog</title>
</head>
<body>
<%= yield %>
</body>
</html>
A Basic Layout
Just yield where you want to insert
the page content
30. The Revised Add Form
This code is inserted into the layout by Rails
to create a full page
31. <h1>Add an Article</h1>
<% form_for @article do |f| %>
<%= f.error_messages %>
<%= f.label :title %><br><%= f.text_field :title %><br>
<%= f.label :body %><br><%= f.text_area :body %><br>
<%= f.submit "Post Article" %>
<% end %>
The Revised Add Form
This code is inserted into the layout by Rails
to create a full page
32. The Revised Edit Form
There’s still some duplication,
but things are definitely improving
33. <h1>Update Article</h1>
<% form_for @article do |f| %>
<%= f.error_messages %>
<%= f.label :title %><br><%= f.text_field :title %><br>
<%= f.label :body %><br><%= f.text_area :body %><br>
<%= f.submit "Save Article" %>
<% end %>
The Revised Edit Form
There’s still some duplication,
but things are definitely improving
35. Fixing the Title
<% content_for :name, "Content" %>
<% content_for :name do %>
<script type="text/javascript"
charset="utf-8">
// ...
</script>
<% end %>
<%= yield :name %>
36. Fixing the Title
content_for() can be
used to pass content <% content_for :name, "Content" %>
between files <% content_for :name do %>
<script type="text/javascript"
charset="utf-8">
// ...
</script>
<% end %>
<%= yield :name %>
37. Fixing the Title
content_for() can be
used to pass content <% content_for :name, "Content" %>
between files <% content_for :name do %>
<script type="text/javascript"
charset="utf-8">
One file sets content, // ...
</script>
using a Ruby String or <% end %>
a block of HTML
<%= yield :name %>
38. Fixing the Title
content_for() can be
used to pass content <% content_for :name, "Content" %>
between files <% content_for :name do %>
<script type="text/javascript"
charset="utf-8">
One file sets content, // ...
</script>
using a Ruby String or <% end %>
a block of HTML
Another file yields to it <%= yield :name %>
by name
40. <% content_for :page_title, "Add an Article" %>
<h1>Add an Article</h1>
<!-- .... -->
<% content_for :page_title, "Update Article" %>
<h1>Update Article</h1>
<!-- ... -->
Set Title Content
Each page sets relevant title content
41. Read the Title Content
The layout will now make use of the title content
if it exists
42. <title>
<%= ["My Blog", yield(:page_title)].compact.join(" : ") %>
</title>
Read the Title Content
The layout will now make use of the title content
if it exists
44. Content Sharing in Action
We now have dynamic
titles based on the
page you are viewing
45. Content Sharing in Action
We now have dynamic
titles based on the
page you are viewing
46. Content Sharing in Action
We now have dynamic
titles based on the
page you are viewing
47. Content Sharing in Action
We now have dynamic
titles based on the
page you are viewing
content_for() is also
handy for sidebars and
other shared content
50. <h1>Update Article</h1>
<% form_for @article do |f| %>
<%= f.error_messages %>
<%= f.label :title %><br><%= f.text_field :title %><br>
<%= f.label :body %><br><%= f.text_area :body %><br>
<%= f.submit "Save Article" %>
<% end %>
Duplicate Form Fields
We need to remove more duplication,
but be pragmatic about what to leave
51. <h1>Update Article</h1>
<% form_for @article do |f| %>
<%= f.error_messages %>
<%= f.label :title %><br><%= f.text_field :title %><br>
<%= f.label :body %><br><%= f.text_area :body %><br>
<%= f.submit "Save Article" %>
<% end %>
Duplicate Form Fields
We need to remove more duplication,
but be pragmatic about what to leave
52. <h1>Update Article</h1>
<% form_for @article do |f| %>
<%= f.error_messages %>
<%= f.label :title %><br><%= f.text_field :title %><br>
<%= f.label :body %><br><%= f.text_area :body %><br>
<%= f.submit "Save Article" %>
<% end %>
Duplicate Form Fields
We need to remove more duplication,
but be pragmatic about what to leave
55. Shared HTML
Any shared HTML can be placed into a “partial”
This is often used for form fields, and code that
displays the details of an individual model
56. Shared HTML
Any shared HTML can be placed into a “partial”
This is often used for form fields, and code that
displays the details of an individual model
That partial can then be inserted into all needed places
57. Shared HTML
Any shared HTML can be placed into a “partial”
This is often used for form fields, and code that
displays the details of an individual model
That partial can then be inserted into all needed places
By convention, partial files begin with an _ in Rails (for
example: _article.html.erb)
59. <%= f.error_messages %>
<%= f.label :title %><br><%= f.text_field :title %><br>
<%= f.label :body %><br><%= f.text_area :body %><br>
_form.html.erb
I’ve moved the form fields into a separate HTML
file, starting with an _ so Rails knows it’s a partial
60. <%= f.error_messages %>
<%= f.label :title %><br><%= f.text_field :title %><br>
<%= f.label :body %><br><%= f.text_area :body %><br>
_form.html.erb
I’ve moved the form fields into a separate HTML
file, starting with an _ so Rails knows it’s a partial
61. Forms render() the Partial
We can render() the partial anywhere we need
to reuse it and even pass variables into it
62. <% content_for :page_title, "Add an Article" %>
<h1>Add an Article</h1>
<% form_for @article do |f| %>
<%= render "form", :f => f %>
<%= f.submit "Post Article" %>
<% end %>
<% content_for :page_title, "Update Article" %>
<h1>Update Article</h1>
<% form_for @article do |f| %>
<%= render "form", :f => f %>
<%= f.submit "Save Article" %>
<% end %>
Forms render() the Partial
We can render() the partial anywhere we need
to reuse it and even pass variables into it
63. <% content_for :page_title, "Add an Article" %>
<h1>Add an Article</h1>
<% form_for @article do |f| %>
<%= render "form", :f => f %>
<%= f.submit "Post Article" %>
<% end %>
<% content_for :page_title, "Update Article" %>
<h1>Update Article</h1>
<% form_for @article do |f| %>
<%= render "form", :f => f %>
<%= f.submit "Save Article" %>
<% end %>
Forms render() the Partial
We can render() the partial anywhere we need
to reuse it and even pass variables into it
66. Partials for Models
Rails is smart about partials used to show a model
It can recognize them by name (more conventions!)
67. Partials for Models
Rails is smart about partials used to show a model
It can recognize them by name (more conventions!)
It will render() the proper partial for a model or
repeatedly render() the same partial for an entire
collection of models
68. Partials for Models
Rails is smart about partials used to show a model
It can recognize them by name (more conventions!)
It will render() the proper partial for a model or
repeatedly render() the same partial for an entire
collection of models
A local variable is set holding the model, again named
by the type
70. <h1>Articles</h1>
<ul>
<% @articles.each do |article| %>
<li>
<%= link_to h(article.title), article_path(article) %>
<%= link_to "edit", edit_article_path(article) %>
</li>
<% end %>
</ul>
Manual Iteration
This code works, but Rails is smart enough to
help us if we follow some conventions
72. <li>
<%= link_to h(article.title), article_path(article) %>
<%= link_to "edit", edit_article_path(article) %>
</li>
_article.html.erb
I moved the Article display code into
an _article.html.erb partial
73. <li>
<%= link_to h(article.title), article_path(article) %>
<%= link_to "edit", edit_article_path(article) %>
</li>
_article.html.erb
I moved the Article display code into
an _article.html.erb partial
74. Partial Found by Name
Rails looks for an _article.html.erb to render()
the Article (matching the names)
75. <h1>Articles</h1>
<ul>
<% @articles.each do |article| %>
<%= render article %>
<% end %>
</ul>
Partial Found by Name
Rails looks for an _article.html.erb to render()
the Article (matching the names)
76. One Step Further
Rails can even recognize a collection (an Array),
render()ing the partial once for each member
77. <h1>Articles</h1>
<ul>
<%= render @articles %>
</ul>
One Step Further
Rails can even recognize a collection (an Array),
render()ing the partial once for each member
80. Where to Hide View Logic
Views should be pretty dumb template filling code
81. Where to Hide View Logic
Views should be pretty dumb template filling code
Logic in your views is hard to maintain and needs to be
moved
82. Where to Hide View Logic
Views should be pretty dumb template filling code
Logic in your views is hard to maintain and needs to be
moved
Move business logic into model methods
83. Where to Hide View Logic
Views should be pretty dumb template filling code
Logic in your views is hard to maintain and needs to be
moved
Move business logic into model methods
If it’s really view logic, write a helper method
84. Where to Hide View Logic
Views should be pretty dumb template filling code
Logic in your views is hard to maintain and needs to be
moved
Move business logic into model methods
If it’s really view logic, write a helper method
A helper is just a Ruby “Mixin” Rails adds to the view
85. These can be Combined
This is some logic though, so it belongs in a
helper method
86. <% content_for :page_title, "Add an Article" %>
<h1>Add an Article</h1>
<% form_for @article do |f| %>
<%= render "form", :f => f %>
<%= f.submit "Post Article" %>
<% end %>
<% content_for :page_title, "Update Article" %>
<h1>Update Article</h1>
<% form_for @article do |f| %>
<%= render "form", :f => f %>
<%= f.submit "Save Article" %>
<% end %>
These can be Combined
This is some logic though, so it belongs in a
helper method
87. <% content_for :page_title, "Add an Article" %>
<h1>Add an Article</h1>
<% form_for @article do |f| %>
<%= render "form", :f => f %>
<%= f.submit "Post Article" %>
<% end %>
<% content_for :page_title, "Update Article" %>
<h1>Update Article</h1>
<% form_for @article do |f| %>
<%= render "form", :f => f %>
<%= f.submit "Save Article" %>
<% end %>
These can be Combined
This is some logic though, so it belongs in a
helper method
88. Adding a Helper Method
I added this method to the Module (“Mixin”) in
app/helpers/application_helper.rb
89. module ApplicationHelper
def page_title(title)
content_for :page_title, title
"<h1>#{title}</h1>"
end
end
Adding a Helper Method
I added this method to the Module (“Mixin”) in
app/helpers/application_helper.rb
90. Switch to Using the Helper
The views are a little cleaner now with the logic
moved to the helper
91. <%= page_title "Add an Article" %>
<% form_for @article do |f| %>
<%= render "form", :f => f %>
<%= f.submit "Post Article" %>
<% end %>
<%= page_title "Update Article" %>
<% form_for @article do |f| %>
<%= render "form", :f => f %>
<%= f.submit "Save Article" %>
<% end %>
Switch to Using the Helper
The views are a little cleaner now with the logic
moved to the helper
94. Built-in Helpers
Rails comes with a ton of helpers, available in all views
Date and time methods
95. Built-in Helpers
Rails comes with a ton of helpers, available in all views
Date and time methods
Number and currency methods
96. Built-in Helpers
Rails comes with a ton of helpers, available in all views
Date and time methods
Number and currency methods
Link and form builders
97. Built-in Helpers
Rails comes with a ton of helpers, available in all views
Date and time methods
Number and currency methods
Link and form builders
Image, CSS, and JavaScript support methods
98. Built-in Helpers
Rails comes with a ton of helpers, available in all views
Date and time methods
Number and currency methods
Link and form builders
Image, CSS, and JavaScript support methods
…
99. An Article Show Page
Uses helpers to escape HTML, show time, and
add simple formatting (like paragraphs) here
100. <%= page_title h(@article.title) %>
<p>posted <%= time_ago_in_words @article.created_at %> ago</p>
<%= simple_format @article.body %>
An Article Show Page
Uses helpers to escape HTML, show time, and
add simple formatting (like paragraphs) here
101. <%= page_title h(@article.title) %>
<p>posted <%= time_ago_in_words @article.created_at %> ago</p>
<%= simple_format @article.body %>
An Article Show Page
Uses helpers to escape HTML, show time, and
add simple formatting (like paragraphs) here
102. <%= page_title h(@article.title) %>
<p>posted <%= time_ago_in_words @article.created_at %> ago</p>
<%= simple_format @article.body %>
An Article Show Page
Uses helpers to escape HTML, show time, and
add simple formatting (like paragraphs) here
103. <%= page_title h(@article.title) %>
<p>posted <%= time_ago_in_words @article.created_at %> ago</p>
<%= simple_format @article.body %>
An Article Show Page
Uses helpers to escape HTML, show time, and
add simple formatting (like paragraphs) here
106. class ArticlesController < ApplicationController
# ...
def show
@article = Article.find(params[:id])
end
def edit
@article = Article.find(params[:id])
end
def update
@article = Article.find(params[:id])
# ...
end
def destroy
@article = Article.find(params[:id])
# ...
end
end
Controller Duplication
It’s very common for show, edit, update, and
destroy to start with the same lookup code
107. class ArticlesController < ApplicationController
# ...
def show
@article = Article.find(params[:id])
end
def edit
@article = Article.find(params[:id])
end
def update
@article = Article.find(params[:id])
# ...
end
def destroy
@article = Article.find(params[:id])
# ...
end
end
Controller Duplication
It’s very common for show, edit, update, and
destroy to start with the same lookup code
109. Before or After an Action
Rails has filters that can be run before or after an action
110. Before or After an Action
Rails has filters that can be run before or after an action
before_filter() is often used to lookup model instances
or check access control
111. Before or After an Action
Rails has filters that can be run before or after an action
before_filter() is often used to lookup model instances
or check access control
You can choose to skip the action that follows
112. Before or After an Action
Rails has filters that can be run before or after an action
before_filter() is often used to lookup model instances
or check access control
You can choose to skip the action that follows
after_filter() isn’t used as much, but it can be handy
for statistics tracking
114. class ArticlesController < ApplicationController
before_filter :find_article, :only => %w[show edit update destroy]
# ...
def show
end
def edit
end
def update
# ...
end
def destroy
# ...
end
private
def find_article
@article = Article.find(params[:id])
end
end
Using a before_filter()
You can specify a method to call before
certain actions are run
115. class ArticlesController < ApplicationController
before_filter :find_article, :only => %w[show edit update destroy]
# ...
def show
end
def edit
end
def update
# ...
end
def destroy
# ...
end
private
def find_article
@article = Article.find(params[:id])
end
end
Using a before_filter()
You can specify a method to call before
certain actions are run
116. class ArticlesController < ApplicationController
before_filter :find_article, :only => %w[show edit update destroy]
# ...
def show
end
def edit
end
def update
# ...
end
def destroy
# ...
end
private
def find_article
@article = Article.find(params[:id])
end
end
Using a before_filter()
You can specify a method to call before
certain actions are run
117. class ArticlesController < ApplicationController
before_filter :find_article, :only => %w[show edit update destroy]
# ...
def show
end
def edit
end
def update
# ...
end
def destroy
# ...
end
private
def find_article
@article = Article.find(params[:id])
end
end
Using a before_filter()
You can specify a method to call before
certain actions are run