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.
13. Solutions
Rails has many different tools to reduce repetition
Layouts
Partials
14. Solutions
Rails has many different tools to reduce repetition
Layouts
Partials
Helpers
15. Solutions
Rails has many different tools to reduce repetition
Layouts
Partials
Helpers
Filters
16. Solutions
Rails has many different tools to reduce repetition
Layouts
Partials
Helpers
Filters
Letās take a look at what each of these is good for
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_ļ¬eld :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 deļ¬nitely improving
33. <h1>Update Article</h1>
<% form_for @article do |f| %>
<%= f.error_messages %>
<%= f.label :title %><br><%= f.text_ļ¬eld :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 deļ¬nitely 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 ļ¬les <% 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 ļ¬les <% content_for :name do %>
<script type="text/javascript"
charset="utf-8">
One ļ¬le 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 ļ¬les <% content_for :name do %>
<script type="text/javascript"
charset="utf-8">
One ļ¬le sets content, // ...
</script>
using a Ruby String or <% end %>
a block of HTML
Another ļ¬le 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_ļ¬eld :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_ļ¬eld :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_ļ¬eld :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 ļ¬elds, 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 ļ¬elds, 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 ļ¬elds, and code that
displays the details of an individual model
That partial can then be inserted into all needed places
By convention, partial ļ¬les begin with an _ in Rails (for
example: _article.html.erb)
59. <%= f.error_messages %>
<%= f.label :title %><br><%= f.text_ļ¬eld :title %><br>
<%= f.label :body %><br><%= f.text_area :body %><br>
_form.html.erb
Iāve moved the form ļ¬elds into a separate HTML
ļ¬le, starting with an _ so Rails knows itās a partial
60. <%= f.error_messages %>
<%= f.label :title %><br><%= f.text_ļ¬eld :title %><br>
<%= f.label :body %><br><%= f.text_area :body %><br>
_form.html.erb
Iāve moved the form ļ¬elds into a separate HTML
ļ¬le, 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 ļ¬lling code
81. Where to Hide View Logic
Views should be pretty dumb template ļ¬lling 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 ļ¬lling 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 ļ¬lling 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 ļ¬lling 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.ļ¬nd(params[:id])
end
def edit
@article = Article.ļ¬nd(params[:id])
end
def update
@article = Article.ļ¬nd(params[:id])
# ...
end
def destroy
@article = Article.ļ¬nd(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.ļ¬nd(params[:id])
end
def edit
@article = Article.ļ¬nd(params[:id])
end
def update
@article = Article.ļ¬nd(params[:id])
# ...
end
def destroy
@article = Article.ļ¬nd(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 ļ¬lters that can be run before or after an action
110. Before or After an Action
Rails has ļ¬lters that can be run before or after an action
before_ļ¬lter() is often used to lookup model instances
or check access control
111. Before or After an Action
Rails has ļ¬lters that can be run before or after an action
before_ļ¬lter() 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 ļ¬lters that can be run before or after an action
before_ļ¬lter() is often used to lookup model instances
or check access control
You can choose to skip the action that follows
after_ļ¬lter() isnāt used as much, but it can be handy
for statistics tracking
114. class ArticlesController < ApplicationController
before_ļ¬lter :ļ¬nd_article, :only => %w[show edit update destroy]
# ...
def show
end
def edit
end
def update
# ...
end
def destroy
# ...
end
private
def ļ¬nd_article
@article = Article.ļ¬nd(params[:id])
end
end
Using a before_ļ¬lter()
You can specify a method to call before
certain actions are run
115. class ArticlesController < ApplicationController
before_ļ¬lter :ļ¬nd_article, :only => %w[show edit update destroy]
# ...
def show
end
def edit
end
def update
# ...
end
def destroy
# ...
end
private
def ļ¬nd_article
@article = Article.ļ¬nd(params[:id])
end
end
Using a before_ļ¬lter()
You can specify a method to call before
certain actions are run
116. class ArticlesController < ApplicationController
before_ļ¬lter :ļ¬nd_article, :only => %w[show edit update destroy]
# ...
def show
end
def edit
end
def update
# ...
end
def destroy
# ...
end
private
def ļ¬nd_article
@article = Article.ļ¬nd(params[:id])
end
end
Using a before_ļ¬lter()
You can specify a method to call before
certain actions are run
117. class ArticlesController < ApplicationController
before_ļ¬lter :ļ¬nd_article, :only => %w[show edit update destroy]
# ...
def show
end
def edit
end
def update
# ...
end
def destroy
# ...
end
private
def ļ¬nd_article
@article = Article.ļ¬nd(params[:id])
end
end
Using a before_ļ¬lter()
You can specify a method to call before
certain actions are run