Last week I went over how to configure and include templates. This is a pretty standard procedural solution: My current template calls another template. But what if I need to pass additional data to my layout template? Perhaps in addition to the content in my template, I also have some <meta> or <script> tags to include, or some <style> specific to this page. This would involve somehow passing data "up", or making the data available for the layout template to use. Mojolicious provides a way to do this: named content blocks.

Content blocks

Layout templates are the most common way of re-using a template. Layout templates wrap around the page's content and provide a common framing with header, footer, navigation, metadata, and otherwise. So layout templates necessarily get re-used quite a lot.

In addition to the main content, displayed with the content helper, I can add other content blocks to my layout that templates can add to using the content_for helper. Frequently this is used for putting additional content like stylesheets or JavaScript in <head>:

@@ layouts/default.html.ep
<!DOCTYPE html>
<head>
    <title><%= title %></title>
    %# Put `content_for 'head'` here
    %= content 'head'
</head>
<body>
    %= content
</body>

@@ jquery-datatable.html.ep
%# Add sorting and searching to a table using the DataTables
%# library: http://datatables.net
% content_for head => begin
    %= stylesheet '//cdn.datatables.net/1.10.19/css/jquery.dataTables.min.css'
    %= javascript '//cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js'
% end
%= include 'table', class => 'data-table'
%= javascript begin
    $(document).ready( function () {
        $('.data-table').DataTable();
    } );
% end

Template Inheritance

Named content blocks can also be used to create additional layers of templates. A generic template defines one or more named content section, and then a template can extend the original by filling in those blocks.

Using the extends helper I can build some simple pages with blocks to fill in (again, much like a layout). Unlike a layout, extending templates always involves named content blocks. The default content is reserved for the top-most layout.

For example, I can build a parent template that provides a two-column layout:

@@ page/two-column.html.ep
%# This template provides content blocks named "left" and "right"
%# for the left and right column, respectively
<div style="display: flex">
    <div><%= content 'left' %></div>
    <div><%= content 'right' %></div>
</div>

@@ articles.html.ep
% extends 'page/two-column';
% layout 'default';
% my $latest = $items->[0];
% content_for left => begin
    <h1>Latest article: <%= $latest->{title} %></h1>
    <p><%== $latest->{html} %></p>
% end
% content_for right => begin
    <h1>Past Articles</h1>
    %= include 'table' => items => [ @{$items}[ 1..$#$items ] ]
% end

I can also extend layouts to achieve the same result. This time I create a new layout called 'two-column' which extends the 'default' layout and is used by my articles template:

@@ layouts/two-column.html.ep
%# This layout provides content blocks named "left" and "right"
%# for the left and right column, respectively
% extends 'layouts/default';
<div style="display: flex">
    <div><%= content 'left' %></div>
    <div><%= content 'right' %></div>
</div>

@@ articles.html.ep
% layout 'two-column';
% my $latest = $items->[0];
% content_for left => begin
    <h1>Latest article: <%= $latest->{title} %></h1>
    <p><%== $latest->{html} %></p>
% end
% content_for right => begin
    <h1>Past Articles</h1>
    %= include 'table' => items => [ @{$items}[ 1..$#$items ] ]
% end

The result is the same, so which method you use is a matter of preference and convenience.

So, to maximize the reusability of your view layer, remember:

  • Stash values can be used to configure templates
  • Stash values are passed down to included templates (include)
  • Includes can accept additional stash values visible only to the included template
  • Named content blocks allow content to be passed up to parent templates (extends) or layout templates (layout)
  • Create and append to named content blocks using content_for 'name', ...
  • The main content helper is reserved for the upper-most layout template

With more robust templates, I can write less code and change my site quickly and easily!

Image by Ivan (https://www.stockvault.net/photo/143213/building-blueprint), modified by Doug Bell. Used under StockVault Non-Commercial license.

Tagged in : templates, rendering

author image
Doug Bell

Doug (preaction) is a long time Perl user. He is the current maintainer of CPAN Testers and the author of many CPAN modules including the Statocles blog engine that powers this site.