0% found this document useful (0 votes)
65 views31 pages

Action View - The Rails 5 Way, Fourth Edition

The document discusses Action View in Rails, which generates the visual output of a Rails application. It covers layouts and templates, template naming conventions, using layouts, and yielding content in views. Action View templates combine data from controllers with HTML and other code to create application output.

Uploaded by

jagadesh.577
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
65 views31 pages

Action View - The Rails 5 Way, Fourth Edition

The document discusses Action View in Rails, which generates the visual output of a Rails application. It covers layouts and templates, template naming conventions, using layouts, and yielding content in views. Action View templates combine data from controllers with HTML and other code to create application output.

Uploaded by

jagadesh.577
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 31

10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

safaribooksonline.com

10. Action View - The Rails 5


Way, Fourth Edition
Safari Books Online
25-32 minutes

10. Action View

The very powerful and the very stupid have one thing in
common. Instead of altering their views to fit the facts,
they alter the facts to fit their views…which can be very
uncomfortable if you happen to be one of the facts that
needs altering.

—Doctor Who

If controllers are the skeleton and musculature of your


Rails application, then models form the heart and mind,
and your view templates (based on Action View, the
third major component of Rails) are your application’s
skin and fashion accessories—the part that is visible to
the outside world.

Action View is the Rails API for putting together the


visual component of your application, namely the HTML
and associated content that will be rendered in a web

1 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

browser whenever someone uses your Rails


application. In this brave new world of REST resources,
Action View is involved in generating almost any sort of
output you generate, not just HTML.

ERb versus Haml

Action View contains a full-featured templating system


based on a Ruby library named ERb. It takes data
prepared by the controller layer and interleaves it with
view code to create a presentation layer for the end
user. It’s also one of the first things you learn about
Rails and part of the standard Ruby library. While ERb
is the Rails standard, I much prefer a templating
solution named Haml[ˆhaml-site] and as such, have
used it all over the book for examples. I think Haml is
such a superior choice over ERb that, other than
mentioning it here, this edition does not cover ERb at
all. Haml, on the other hand, get its own full chapter.

http://haml-lang.com/

In this chapter, we cover the fundamentals of the Action


View framework, from effective use of partials, to the
significant performance boosts possible via caching.

Layouts and Templates

Rails has easy conventions for template usage, related


to the location of templates with the Rails project
directories.

2 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

The app/views directory contains subdirectories


corresponding to the name of controllers in your
application. Within each controller’s view subdirectory,
you place a template named to match its corresponding
action.

The special app/views/layout directory holds layout


templates, intended to be reusable containers for your
views. Again, naming conventions are used to
determine which templates to render, except that this
time it is the name of the controller that is used for
matching.

Template Filename Conventions

The filename of a template in Rails carries a lot of


significance. Its parts, delimited with periods,
correspond to the following information:

• name (usually maps to action)

• locale (optional)

• content type

• templating engine(s)

• variant (optional, new in Rails 4.1)

We’ll get into specifics about this naming scheme a little


further along in the chapter.

Layouts

3 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

A layout is an HTML document container meant to be


reused by many controller’s actions. Simple applications
may have one layout shared by all actions, while more
complex applications may have many layouts, even one
or more per controller.

Action View decides which layout to render based on


the inheritance hierarchy of controllers being executed.
Most Rails applications have an
application.html.haml file in their layout directory,
because it’s part of the files generated by the rails
new bootstrapping script. The base layout shares its
name with the ApplicationController, which is
typically extended by all the other controllers in an
application; therefore it is picked up as the default
layout for all views.

It is picked up, unless of course, a more specific layout


template is in place, but quite often it makes sense to
use just one application-wide template, such as the
simple one shown in Listing 10.1.

Listing 10.1: A simple general-purpose


application.html.haml layout template

!!! 5
%html
%head
%meta{ charset: 'utf-8' }
%title TR4W Time and Expenses Sample

4 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

Application
= csrf_meta_tag
= stylesheet_link_tag 'application',
media: 'all'
= javascript_include_tag
'application'
%body
=yield

Yielding Content

The Ruby language’s built-in yield keyword is put to


good use in making layout and action templates
collaborate. Notice the use of yield at the end of the
layout template:

%body
= yield

In this case, yield by itself is a special message to the


rendering system. It marks where to insert the output of
the action’s rendered output, which is usually the
template corresponding to that action.

You can add extra places in your layout where you want
to be able to yield content by including additional yield
invocations—just make sure to pass a unique identifier
as the argument. A good example is a layout that has
left and right sidebar content (simplified, of course):

5 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

%body
.left.sidebar
= yield :left
.content
= yield
.right.sidebar
= yield :right

The .content div receives the main template markup


generated. But how do you give Rails content for the left
and right sidebars? Easy—just use the content_for
method anywhere in your template code. I usually stick
it at the top of the template so that it’s obvious.

- content_for :left do
%h2 Navigation
%ul
%li

- content_for :right do
%h2 Help
%p Lorem ipsum dolor sit amet,
consectetur adipisicing elit...

%h1 Page Heading


%p ...

Besides sidebars and other types of visible content


blocks, I suggest you yield for additional content to be
added to the HEAD element of your page, as shown in

6 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

Listing 10.2.

Listing 10.2: Yielding additional head content

!!! 5
%html
%head
%meta{ charset: 'utf-8' }
%title TR4W Time and Expenses Sample
Application
= csrf_meta_tag
= stylesheet_link_tag 'application',
media: 'all'
= javascript_include_tag
'application'
= yield :head
%body
= yield

Kevin says…

Yielding in the HEAD element is also a great technique


to include page specific meta tags, such as those
required for Facebook Open Graph.

Conditional Output

One of the most common idioms you’ll use when coding

7 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

Rails views is to conditionally output content to the view.


The most elementary way to control conditional output
is to use if statements.

- if show_subtitle?
%h2 = article.subtitle

A lot of times you can use inline if conditions and


shorten your code, since the = outputter doesn’t break if
you feed it a nil value. Just add a postfix if condition to
the statement:

%h2 = article.subtitle if show_subtitle?

Of course, there’s a problem with the preceding


example. The if statement on a separate line will
eliminate the <h2> tags entirely, but the one-liner
second example does not.

There are a couple of ways to deal with the problem


and keep it a one-liner. First, there’s the butt-ugly
solution that I’ve occasionally seen in some Rails
applications, which is the only reason why I’m
mentioning it here!

= "<h2> #{h(article.subtitle)} </h2>"


.html_safe if show_subtitle?

A more elegant solution involves Rails’ content_tag


helper method, but admittedly a one-liner is probably
not superior to its two-line equivalent in this case.

= content_tag ('h2', article.subtitle) if

8 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

show_subtitle?

Helper methods, both the ones included in Rails like


content_tag and the ones that you’ll write on your
own, are your main tool for building elegant view
templates. Helpers are covered extensively in Chapter
11, “All About Helpers.”

Decent Exposure

We’ve seen how layouts and yielding content blocks


work, but other than that, how should data get from the
controller layer to the view? During preparation of the
template, instance variables set during execution of the
controller action will be copied over as instance
variables of the template context. Even though it’s the
standard way exposed by Rails documentation, sharing
state via instance variables in controllers promotes
close coupling with views.

Hashrocket’s Decent Exposure gem provides a


declarative manner of exposing an interface to the state
that controllers contain, thereby decreasing coupling
and improving your testability and overall design.

https://github.com/hashrocket/decent_exposure

When invoked, Decent Exposure’s expose macro


creates a method with the given name, evaluates the
provided block and memoizes the result. This method is
then declared as a helper_method so that views may

9 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

have access to it and is made unroutable as an action.


When no block is given, expose attempts to intuit
which resource you want to acquire:

expose :timesheet

Does something kind of like this, just more concisely:

Timesheet.find(params[:timesheet_id] ||
params[:id])

The symbol passed to expose is used to guess the


class name of the object you want to find and related
parameters—useful since almost every controller in a
normal Rails application uses this kind of code in the
show, edit, update and destroy actions.

In a slightly more complicated scenario, you might need


to find an instance of an object using something other
than a simple find method on its Active Record class.
Decent Exposure provides an array of options for
customizing default behavior, including :scope:

expose :client
expose :timesheet, scope: -> {
client.timesheets }

The example says, “search for timesheets in the client’s


association instead of the Timesheet class.” And note
that exposures can refer to each other.

In the RESTful controller paradigm, with its nesting of


resources, you’ll find yourself using this particular idiom

10 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

again and again.

Exposing completely custom code is just a matter of


passing a block.

expose :client
expose :timesheet, scope: -> {
client.timesheets }
expose :timesheet_approval_presenter do

TimesheetApprovalPresenter.new(timesheet,
current_user)
end

or a symbol

expose :client
expose :timesheet, scope: -> {
client.timesheets }
expose :timesheet_approval_presenter,
:setup_presenter
...

private

def setup_presenter
...
TimesheetApprovalPresenter.
new(timesheet, current_user)
end

11 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

The last couple of examples also demonstrate how


expose declarations can depend on each other. In fact,
proper use of expose should eliminate most model-
lookup code from your actual controller actions.

At Hashrocket, use of Decent Exposure has proven so


beneficial that it completely replaced direct use of
instance variables in controllers and views. The helper
methods created by the expose macro are just referred
to directly in the view.

Standard Instance Variables

More than just instance variables from the controller are


copied over to the template.

assigns

Want to see everything that comes across the


controller-view boundary? Throw = debug(assigns)
into your template and take a look at the output. The
assigns attribute is essentially internal to Rails, and
you should not use it directly in your production code.

base_path

Local filesystem path pointing to the base directory of


your application where templates are kept.

controller

The current controller instance is made available via


controller, before it goes out of scope at the end of

12 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

request processing. You can take advantage of the


controller’s knowledge of its name (via the
controller_name attribute) and the action that was
just performed (via the action_name attribute), in
order to structure your CSS more effectively.

%body { class:
"#{controller.controller_name}
#{controller.action_name}" }

That would result in a BODY tag looking something like


this, depending on the action executed:

< body class="timesheets index ">

Note

You could also replicate the functionality in the previous


example by using the Haml helper method
page_class.

%body { class: page_class }

Hopefully you already know that the C in CSS stands


for cascading, which refers to the fact that class names
cascade down the tree of elements in your markup code
and are available for creation of rules. The trick is to
automatically include the controller and action name as
classnames of your body element, so that you can use
them to customize the look and feel of the page very
flexibly later on in the development cycle. For example,
here’s how you would use the technique to vary the

13 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

background of header elements depending on the


controller path in SCSS:

body {
.timesheets .header {
background: image_url(timesheet-
bg.png) no-repeat left top ;
}

.expense_reports .header {
background: image_url(expense-
reports-bg.png) no-repeat left top;
}
}

cookies

The cookies variable is a hash containing the user’s


cookies. There might be situations where it’d be okay to
pull values out to affect rendering, but most of the time
you’ll be using cookies in your controller, not the view.

flash

The flash has popped up in larger code samples


throughout the book so far, whenever you want to send
the user a message from the controller layer but only for
the duration of the next request.

def create
if user.try(:authorize, params[:user]
[:password])

14 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

redirect_to home_url, notice:


"Welcome, #{user.first_name}!"
else
redirect_to :new, alert: "Login
invalid."
end
end

A common Rails practice is to use flash[:notice] to


hold benign notice messages, and flash[:alert] for
communication of a more serious nature.

Note

It’s so common to set flash notice and alert


messages on redirects that Rails enables you to set
them in the redirect_to method as optional
parameters.

def create
if user.try(:authorize, params[:user]
[:password])
redirect_to home_url, notice:
"Welcome, #{user.first_name}!"
else
redirect_to home_url, alert: "Bad
login"
end
end

15 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

Special accessors for notices and alerts are included as


helper methods on the flash object itself, since their use
is so common.

def create
if user.try(:authorize, params[:user]
[:password])
redirect_to home_url, notice:
"Welcome, #{user.first_name}!"
else
redirect_to action: "new ", alert:
"Login invalid."
end
end

Displaying flash messages

Personally, I like to conditionally output both notice


and alert messages in div elements, right at the top
of my layout, and use CSS to style them as shown in
Listing 10.3:

Listing 10.3: Standardized flash notice and error


placement in application.html.haml

%html
...
%body
- if flash.notice
.notice= flash.notice

16 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

- if flash.alert
.notice.alert= flash.alert

= yield

The CSS for .notice defines most of the style for the
element, and .alert overrides just the aspects that
are different for alerts.

flash.now

Sometimes you want to give the user a flash message


but only for the current request. In fact, a common
newbie Rails programming mistake is to set a flash
notice and not redirect, thereby incorrectly showing a
flash message on the following request.

It is possible to make flash cooperate with a render by


using the flash.now method.

class ReportController <


ActionController::Base
def create
if report.save
redirect_to report_path(report),
notice: "#{report.title} has been
created\
."
else
flash.now.alert = "#{report.title}
could not be created."

17 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

render :new
end
end
end

The flash.now object also has notice and alert


accessors, like its traditional counterpart.

logger

Have something to record for posterity in the logs while


you’re rendering the view? Use the logger method to
get the view’s Logger instance, the same as
Rails.logger, unless you’ve changed it.

params

This is the same params hash that is available in your


controller. It’s very dangerous from a security
perspective to put unfiltered parameter data into the
output stream of your template. The following section,
“Protecting the Integrity of Your View from User-
Submitted Content,” covers that topic in depth.

request and response

The HTTP request and response objects are


exposed to the view, but other than for debugging
purposes, I can’t think of any reason why you would
want to use them directly from your template.

session

18 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

The session variable is the user’s session hash.


There might be situations where it’d be okay to pull
values out to affect rendering, but I shudder to think that
you might try to set values in the session from the view
layer. Use with care, and primarily for debugging, just
like request and response.

Partials

A partial is a fragment of template code. The Rails Way


is to use partials to factor view code into modular
chunks that can be assembled in layouts with as little
repetition as possible.

In older versions of Rails, the syntax for including a


partial within a template started with render
:partial, but now passing a string to render within
your view will get interpreted to mean you want to
render a partial. Partial template names must begin with
an underscore, which serves to set them apart visually
within a given view template directory. However, you
leave the underscore out when you refer to them.

%h1 Details
= render 'details'

Simple Use Cases

The simplest partial use case is simply to extract a


portion of template code. Some developers divide their

19 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

templates into logical parts by using partial extraction.


Sometimes it is easier to understand the structure of a
screen if the significant parts are factored out of it.

For instance, Listing 10.4 is a simple user registration


screen that has its parts factored out into partials.

Listing 10.4: Simple user registration form with partials

%h1 User Registration


= error_messages_for :user
= form_for :user, url: users_path do
.registration
.details.demographics
= render 'details'
= render 'demographics'
.location
= render 'location'
.opt_in
= render 'opt_in'
.terms
= render 'terms'
%p= submit_tag 'Register'

While we’re at it, let me pop open one of those partials.


To conserve space, we’ll take a look at one of the
smaller ones, the partial containing the opt-in check
boxes of this particular app. The source is in Listing
10.5; notice that its name begins with an underscore.

Listing 10.5: The opt-in partial in the file app/views

20 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

/users/_opt_in.html.haml

%fieldset#opt_in
%legend Spam Opt In
%p
= check_box :user,
:send_event_updates
Send me updates about events!
%br
= check_box :user, :send_site_updates
Notify me about new services

Personally, I like partials to be entirely contained inside


a semantically significant markup container. In the case
of the opt-in partial in Listing 10.5, both check box
controls are contained inside a single fieldset
element, which I’ve given an id attribute. Following that
rule, more as a loose guideline than anything else,
helps me to mentally identify how the contents of this
partial are going to fit inside the parent template. If we
were dealing with other markup, perhaps outside of a
form, I might choose to wrap the partial markup inside a
well-identified div container, instead of a fieldset.

Reuse of Partials

Since the registration form is neatly factored out into its


component parts, it is easy to create a simple edit form
using some of its partials, as in Listing 10.6.

21 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

Listing 10.6: Simple user edit form reusing some of the


same partials

%h1 Edit User


= form_for :user, url: user_path(@user),
method: :put do
.settings
.details
= render 'details'
.demographics
= render 'demographics'
.opt_in
= render 'opt_in'
%p = submit_tag 'Save Settings'

If you compare Listings 10.4 and 10.6, you’ll notice that


markup skeleton changed and the page has less
content than the registration form. Perhaps the user’s
location is handled in greater detail on another screen,
and certainly you don’t want to require agreement of
terms every time the user changes her settings.

Shared Partials

Until now we’ve been considering the use of partials


that reside in the same directory as their parent
template. However, you can easily refer to partials that
are in other directories, just by prefixing the directory
name. You still leave off the underscore, which has

22 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

always felt a little weird.

Let’s add a captcha partial to the bottom of the


registration form from Listing 10.4, to help prevent
spammers from invading our web application:

...
.terms
= render 'terms'
.captcha
= render 'shared/captcha'
%p = submit_tag 'Register'

Since the captcha partial is used in various different


parts of the application, it makes sense to let it reside in
a shared folder rather than any particular view folder.
However, you do have to be a little bit careful when you
move existing template code into a shared partial. It’s
quite possible to inadvertently craft a partial that
depends implicitly on where it’s rendered.

For example, take the case of the Rails-talk mailing list


member with a troublesome partial defined in
login/_login.html.haml:

= form_tag do
%fieldset
%label
Username:
= text_field_tag :username,
params[:username]

23 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

%br
%label
Password:
= password_field_tag :password,
params[:password]
%br
= submit_tag "Login"

The login form submission worked when he rendered


this partial as part of the login controller’s login action
(“the login page”), but not when it was included as part
of the view for any other section of his website. The
problem is that form_tag (covered in the next chapter)
takes an optional action parameter telling it where to
post its information. If you leave out the action, the form
will post back to its current URL, which will vary for
shared partials, depending on where they’re being used
from.

An alternate, perhaps more important lesson here is:


give forms embedded in shared partials explicit url
targets.

Passing Variables to Partials

Partials inherit the variables and methods exposed to


their parent templates implicitly. That’s why the form
helpers used in the partials of Listings 10.4 and 10.6
work: They rely implicitly on an user method to be in

24 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

scope. I feel it’s fine to use this implicit sharing in some


cases, particularly when the partials are tightly bound to
their parent templates. It would be especially true in
cases where the only reason you broke out a partial in
the first place was to reduce the size and complexity of
a particularly large template.

However, once you get into the practice of breaking out


partial templates for reuse, depending on implicit
context gets a lot more dicey. That’s why Rails supports
the passing of locally scoped variables to partial
templates, as in the following snippet:

= render 'shared/address', form: form

The values of the optional hash are converted into


locally scoped variables (no @ sign) in the partial.

Listing 10.7 is a variation on the registration template.


This time we’re using the version of form_for that
yields a block parameter representing the form to its
form helper methods. We’ll pass that form parameter
on, too.

Listing 10.7: Simple user registration template passing


form as local variable

%h1 User Registration


= form_for :user, url: users_path do
|form|
.registration
.details.address.demographics

25 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

= render 'details', form: form


= render 'shared/address', form:
form
%p = form.submit 'Register'

And finally, in Listing 10.8 we have the shared address


form.

Listing 10.8: A simple shared address partial using local


variable

%fieldset .address
%legend Address
%p
%label Street
%br
= form.text_area :street, rows: 2,
cols: 40
%p
%label City
%br
= form.text_field :city
%p
%label State
%br
= form.text_field :state, size: 2
%p
%label Zip
%br

26 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

= form.text_field :zip, size: 15

The form helper methods, which we’ll cover in Chapter


11, “All About Helpers,” have a variation in which they
are called on the form variable yielded by the
form_for method. That is exactly what we passed on
to these partials.

The local_assigns Hash

If you need to check for the presence of a certain local


variable in a partial, you can do it by checking the
local_assigns hash that is part of every template.
Using the Ruby idiom defined? variable won’t
work due to limitations of the rendering system.

- if local_assigns.has_key? :special
= special

Rendering an Object

The render method also provides a shorthand syntax


for rendering an object into a partial, which strictly
depends on Rails naming conventions.

= render entry

Rails magic alert!

The partial corresponding to the last code snippet is


named _entry.html.haml and gets a local variable

27 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

named entry. This is equivalent to the following:

= render partial: 'entry', object: entry

To set a different local variable name other than the


name of the partial, you could use the locals hash as
seen earlier in the chapter or specify the desired name
through the :as option.

= render partial: 'entry', object:


some_entry, as: :item

Rendering Collections

One of the best uses of partials is to render collections.


Once you get into the habit of rendering collections with
partials, you won’t want to go back to the relative
ugliness of cluttering your templates with for loops and
each.

When the render method gets an Enumerable as its


first argument, it assumes that you want to render a
collection of partials.

= render entries

This is simple and precise yet very dependent on a


naming conventions. The objects being rendered are
exposed to the partial template as a local variable
named the same as the partial template itself. In turn
the template should be named according to the class of
the objects being rendered.

28 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

The partial corresponding to the last code snippet is


named _entry.html.haml and gets a local variable
named entry.

= div_for(entry) do
= entry.description
#{distance_of_time_in_words_to_now
entry.created_at} ago

Kevin says…

If the collection passed into the render method is


empty, nil is returned. Using this knowledge, you can
write code such as

= render(entries) || "No entires exist"

to provide fallback content.

Since the partial template used is based on the class of


each item, you can easily render a heterogeneous
collection of objects. This technique is particularly useful
in conjunction with collections of STI subclasses.

If you want to override that behavior, then revert to the


older partial syntax and specify the :partial and
:collection options explicitly like this:

= render partial: 'entry ', collection:


@entries

The partial_counter Variable

29 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

There’s another variable set for collection-rendered


partials that doesn’t get much attention. It’s a 0-indexed
counter variable that tracks the number of times a
partial has been rendered. It’s useful for rendering
numbered lists of things. The name of the variable is the
name of the partial, plus _counter.

= div_for(entry) do
"#{entry_counter}:#{entry.description}
#{distance_of_time_in_words_to_now
entry.created_at} ago"

Sharing Collection Partials

If you wanted to use the same partial that you use with
a collection, except with a single entry object, you’d
have to pass it that single instance via the locals hash
described in the preceding section, like this:

= render 'entry', entry: some_entry

Logging

If you take a look at your development log, you’ll notice


that it shows which partials have been rendered and
how long they took.

Rendering template within


layouts/application
Rendering listings/index

30 of 31 11/12/17, 3:55 PM
10. Action View - The Rails 5 Way, Fourth Edition about:reader?url=https://www.safaribooksonline.com/library/vie...

Rendered listings/_Listing 0.6ms


Rendered listings/_Listing 0.3ms
Rendered listings/_Listing 0.2ms
Rendered listings/_Listing 0.2ms
Rendered listings/_Listing 0.2ms
Rendered layouts/_login 2.4ms
Rendered layouts/_header 3.3ms
Rendered layouts/_footer 0.1ms1

Conclusion

In this chapter, we’ve covered the Action View


framework with an explanation of templating and how
the Rails rendering system works. We’ve also covered
the use of partials in-depth, since their use is essential
for effective Rails programming.

Now it’s time to cover the mechanism whereby you can


inject a whole bunch of smarts into your view layer
without cluttering up your templates: Helpers.

31 of 31 11/12/17, 3:55 PM

You might also like