Modifying Document Properties in Nested DocPad Layouts
DocPad Layout Basics
One of the main DocPad features is support for layouts, which are used to ensure a common design for multiple pages. This is an example of a simple layout:
<!DOCTYPE html>
<html lang="en">
<head>
<title><%= @document.title %></title>
</head>
<body>
<h1>Site Name</h1>
<h2><%= @document.title %></h2>
<%- @content %>
</body>
</html>
It's using Eco (Embedded CoffeeScript) templating engine and shouldn't be difficult to understand even if you're seeing it for the first time.
This could be a page using this layout:
---
layout: "base"
title: "Page Title"
---
<p>Page content</p>
The header part specifies the name of the layout to use and sets document properties, which are accessed in the layout using the @document.propertyName
syntax. The rest of the page replaces @content
in the layout file. Here's the resulting generated page:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Page Title</title>
</head>
<body>
<h1>Site Name</h1>
<h2>Page Title</h2>
<p>Page content</p>
</body>
</html>
Nested Layouts
Layouts can even be nested, which opens doors to a new set options. A typical scenario for using them would be several different types of pages in a single site with a common basic design; e.g. articles could always display their author:
---
layout: "base"
---
<p>Author: <%= @document.author %></p>
<p>Published on: <%= @document.date %></p>
<%- @content %>
The sub-layout will get injected into the base layout. The page will need to define the additional properties, expected by it:
---
layout: "article"
title: "Article Title"
author: "Damir Arh"
---
<p>Page content</p>
The page will be generated as one would intuitively expect:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Page Title</title>
</head>
<body>
<h1>Site Name</h1>
<h2>Page Title</h2>
<p>Author: Damir Arh</p>
<p>Page content</p>
</body>
</html>
Common Document Properties in Layouts
Once I started using nested layouts, I soon wanted to set certain document properties in the sub-layout instead of directly on the page, because the value was common to all pages based on that layout and I didn't want to repeat myself.
Section name is a good example of such a property. The base layout would take care of displaying it:
<!DOCTYPE html>
<html lang="en">
<head>
<title><%= @document.title %></title>
</head>
<body>
<h1>Site Name - <%= @document.section %></h1>
<h2><%= @document.title %></h2>
<%- @content %>
</body>
</html>
To set the section name directly in the page, I could just add it to the header along with all the other properties:
---
layout: "article"
title: "Article Title"
author: "Damir Arh"
section: "Articles"
---
<p>Page content</p>
Somehow I expected this to work even if I would put it in the sub-layout header instead. To my surprise, the property value remained uninitialized. Giving it some more thought, I realized it couldn't have worked: instead of setting a property on the page, I was setting it on the sub-layout. There's a different syntax to access page properties from a layout, and I've already been using it to display them. There's no reason I couldn't set the values the same way:
---
layout: "base"
---
<% @document.section = "Articles" %>
<p>Author: <%= @document.author %></p>
<%- @content %>
Now, the pages don't need to include this value any more:
---
layout: "article"
title: "Article Title"
author: "Damir Arh"
---
<p>Page content</p>
The generated page will still have properly initialized property value:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Page Title</title>
</head>
<body>
<h1>Site Name - Articles</h1>
<h2>Page Title</h2>
<p>Author: Damir Arh</p>
<p>Page content</p>
</body>
</html>
I didn't manage to find any such example online and spent too much time figuring it out. In my opinion, that's a reason enough for writing this blog post.