Reusing code in Rails templates

Altura Soluciones
AlturaSoluciones
Published in
5 min readMar 16, 2018

How to use partials applying DRY(Don’t Repeat Yourself) on Rails views

Rails partials views (those that get rendered in the context of other views) allow us to reuse code. They have several differences to action's views rendered in a controller action.

For example if we have a controller like this:

class SamplesController < ApplicationController
def show
render 'show' #this can be omitted
end
end

We should have a a view file inside app/views/samples called show.html.haml with the following content:

= content_for :header do
= render 'extra_header'
Hello World! Server time: #{Date.now.strftime('%m/%d/%Y')}.
= content_for :footer do
= render 'extra_footer'
Note: we are using HAML as markup template language for the view's code examples on this post

On this file we can also see calls to render method as in the controller. However this invocations are from different nature: instead of rendering an action's view it allow us to render a partial inside a particular view. Let’s see the main differences between this two types of views.

Where are partial views?

By default, if no view path is specified, Rails assume app/views/<resource>. For this particular example an action's view is located by default on app/views/samples due to that the controller is called SamplesController.

Partial views like in this example: extra_footer and extra_header, will be also located on the same directory: app/views/samples. The difference is that partial views are prefixed with an underscore so we will have _extra_footer.html.haml and _extra_header.html.haml.

Difference in redering

It’s very important to emphasize that the render method from a controller call it’s not the same that render method called from a view. The views that are rendered from a controller doesn’t need to be prefixed with underscore unlike partials.

Additionally passing local variables is different in each case. For example if we wish to pass a variable foo to an action's view we need to write something like:

class SampleController < ApplicationController
def show
render 'show', locals: {foo: 123.45}
end
end

And on the show.html.haml action view we should modify the content to access the passed local variable:

= content_for :header do
= render 'extra_header'
Hello World!. Server time: #{Date.now.strftime('%m/%d/%Y')}.
The content of foo variable is: #{foo}
= content_for :footer do
= render 'extra_footer'

If we want to pass some parameters to a partial view instead the syntax is slightly different. Assuming that we have the following on _extra_header.hmlt.haml partial to display the content of a local variable called bar:

Hello World! Bar's value is: #{bar}

we must adjust the render call like this:

= content_for :header do
= render 'extra_heading', bar: 678.9
Hello World!. Server time: #{Date.now.strftime('%m/%d/%Y')}.
The content of foo variable is: #{foo}
= content_for :footer do
= render 'extra_footer'

Here the difference is that we don’t need to pass all the variables inside a locals parameter.

Layouts and content_for

Action’s views

When an action's view gets rendered is going to be wrapped inside a layout. A layout is similar to a template that includes most of assets like: .js, .css, .js.coffee, .css.scss and define a base skeleton of our app. These are found in app/views/layouts using application.html.haml by default.

Inside the layout we are going to have html standard content with haml, ruby, javascript … embeded:

!!!
%html
%head
%title Hello!
/ rest of header content and include sentences for .js, .css
/ or other assets
%body
= yield :extra_heading
= yield
= yield :extra_footer

Here we have three yield's sections, but only two of them are named. Although both types of yields give the rendering control to a view the are very different:

  • Their should be only one nameless yield that will give the rendering control of the view that we command to from the controller.
  • We should have for every :name that we want to use a yield :name instruction. These are going to give the rendering execution to every content_for :name that the rendering process find.

Base on this we can stablish that:

  • = yield will render the correspondingaction's view fo the controller, in this case app/views/samples/show
  • = yield :extra_header and = yield :extra_footer are put in a stack to get processed at the end.
  • When the application starts to render the show view it will find two content_for instructions that will render the corresponding yields instructions.

Finally the partial view _extra_header.html.haml get’s rendered inside the instruction = yield :extra_header and the same with the footer.

We will always have a layout action view for our controller methods like render show. Rails will use the application layout app/views/layouts/application.html.haml. To change that behaviour we can specify a new layout to the controller:

class SamplesController < ApplicationConroller
layout 'another_layout'
def show
render 'show', locals: {foo:123.45}
end
end

In this case we should have another layout at app/views/layouts/another_layout.haml that will get rendered for every controller action. If we want to exclude some action from using this new layout and use the default application layout we can use only or excep instructions:

class SamplesController < ApplicationController
layout 'another_layout', except: [:do_something_else,
:do_something_else2]
def show
# the used layout will be another_layout.html.haml
render 'show', locals: {foo:123.45}
end
def edit
# it will render the another.layout.html.haml layout
end
def do_something_else
# it will render application.html.haml layout
end
def do_something_else2
# it will render application.html.haml layout
end
end

Additionally we can specify which layout will be used on the render method. This will have more precedence than the layout specified on the controller.

class SamplesController < ApplicationController
layout 'another_layout', except:[:do_something_else,
:do_something_else2, do_something_else3]
# .
# ..
# ...
def do_something_else3
render layout: 'third_layout'
# it will render third_layout.html.haml
end
end

Partials views

We could say that the rendering of a partial view doesn’t have the ability to wrap inside a layout, like action's view do. Invoking something like:

= render 'mi_partial', layout: 'some_layout'

doesn’t have the same meaning. Why? Because a partial view doesn’t need to get wrapped on some application skeleton.

The first reason is that a partial layout it’s not for that but for showing a piece of data that eventually we can reuse.

Secondly a partial view has the ability to be a layout itself.

Let’s see an example creating a third partial view, that will be used for the header and footer. Inside app/views/samples we should create a file called _extra_section.html.haml with the following content:

%section{class: extra_class}
= yield

This partial is very basic but accepts two premises:

  • It could receive local arguments, as seen before.
  • Accepts content with a = yield instruction likewise an action's view.

The big difference here is when we are going to use the view. For this example in our _extra_header view we would have something like:

# Rails 4
= render layout: 'extra_section', extra_class: 'mi-heading' do
Hello world! Bar's value is #{bar}

# Rails 5 this syntax if used on Rails 4 will not render the content
= render 'extra_section', extra_class: 'mi-heading' do
Hello world! Bar's value is #{bar}

Something analogous can be coded for the footer.

Summing up

There are two ways of reusing code on Rails views so we don’t repeat ourselves (DRY principle).

Action’s views/General layouts: allow us to define the general structure of our site pages. by having a =yield sentence we can render automatically the desired content.

Partial Views: allow us to define a reusable content that can serve as structure in a render sentence(inside a view) as wrapping structure of another content that contains a yield or a yield value sentence we we going to insert the desired content.

Author: Eng. Luis Masuelli

--

--

Altura Soluciones
AlturaSoluciones

IT consulting. Agile and Lean remote software development team specialized in Web, Mobile, React.js and Ruby on Rails from Ecuador.