Layouts and Partials
Learning objectives
- Knows what layouts are and knows how to use them.
- Knows what partials are and knows how to use them.
So far, most of our applications have not contained the full HTML markup, i.e. the html
-element, the body
-element, etc. That is, instead of writing an HTML document that looks like the following: one.
<!DOCTYPE html>
<html>
<head>
<title>Hello there!</title>
</head>
<body>
<h1>The heading!</h1>
<p>The content!</p>
</body>
</html>
We have written something like the following one:
<h1>The heading!</h1>
<p>The content!</p>
This has been intentional. Let's take a brief look into using layouts and partials in Eta, which allow us to add repeating content to our applications.
Layouts
Layouts are a mechanism that allows defining -- well -- a layout for our applications. When working with layouts, we define a layout file that contains the template HTML and a content file that uses the layout.
Let's start with the following project structure. We have a file app.js
in the root folder with a folder views
. The folder views
contains a file index.eta
and a folder layouts
. The folder layouts
contains a file layout.eta
.
tree --dirsfirst
.
├── views
│ ├── layouts
│ │ └── layout.eta
│ └── index.eta
└── app.js
Let's use the file layout.eta
as a layout file. A layout file is defined as a normal HTML document that has an Eta tag <%~ it.body %>
. The tag will be replaced from content from the file that uses the layout file.
The following example -- layout.eta
-- defines a template with generic HTML boilerplate code. The Eta tag <%~ it.body %>
is within the body
element, which means that content from a content file will be placed there.
<!DOCTYPE html>
<html>
<head>
<title>Hello there!</title>
</head>
<body>
<%~ it.body %>
</body>
</html>
Layout files are used from other view templates using the layout
function. The function call receives the path to the layout file as a parameter, and it is called from the top of the view template file.
For example, if the index.eta
file contains the following content, rendering the index.eta
file will lead to a situation where the content in index.eta
below the <% layout("./layouts/layout.eta") %>
will be rendered to the place in layout.eta
specified with the tag <%~ it.body %>
.
<% layout("./layouts/layout.eta") %>
<h1>The heading!</h1>
<p>The content!</p>
Let's assume that the contents of the app.js
is as follows. That is, the app.js
is a normal web application that renders a view template (in this case, index.eta
).
import { serve } from "https://deno.land/std@0.222.1/http/server.ts";
import { configure, renderFile } from "https://deno.land/x/eta@v2.2.0/mod.ts";
configure({
views: `${Deno.cwd()}/views/`,
});
const responseDetails = {
headers: { "Content-Type": "text/html;charset=UTF-8" },
};
const data = {};
const handleRequest = async (request) => {
return new Response(await renderFile("index.eta", data), responseDetails);
};
serve(handleRequest, { port: 7777 });
Now, when we run the application, everything seems to work ok.
deno run --allow-net --allow-read app.js
And, when we access the application, we see the following output.
curl http://localhost:7777
<!DOCTYPE html>
<html>
<head>
<title>Hello there!</title>
</head>
<body>
<h1>The heading!</h1>
<p>The content!</p> </body>
</html>
As we are still working with view templates -- layouts are just a functionality of view templates -- we can add data from the server. Let's change both the layout and the view template so that they will contain data from the server. In the layout, we use a title from the server, while in the view template we use a heading from the server.
<!DOCTYPE html>
<html>
<head>
<title><%= it.title %></title>
</head>
<body>
<%~ it.body %>
</body>
</html>
<% layout("./layouts/layout.eta") %>
<h1><%= it.heading %></h1>
<p>The content!</p>
The title from the server is My Title
and the heading from the server is My Heading
.
import { serve } from "https://deno.land/std@0.222.1/http/server.ts";
import { configure, renderFile } from "https://deno.land/x/eta@v2.2.0/mod.ts";
configure({
views: `${Deno.cwd()}/views/`,
});
const responseDetails = {
headers: { "Content-Type": "text/html;charset=UTF-8" },
};
const data = {
title: "My Title",
heading: "My Heading",
};
const handleRequest = async (request) => {
return new Response(await renderFile("index.eta", data), responseDetails);
};
serve(handleRequest, { port: 7777 });
Now, when when we run the application and access it, the output will contain the data from the server.
deno run --allow-net --allow-read app.js
curl http://localhost:7777
<!DOCTYPE html>
<html>
<head>
<title>My Title</title>
</head>
<body>
<h1>My Heading</h1>
<p>The content!</p> </body>
</html>
Partials
Partials are used for dividing HTML into separate logical entities. They are useful, for example, for easier content management, allowing the developer to work on a single logical part of the HTML code at a time.
Let's continue with the previous example with a layout file and a view template, and create a partial for a menu. Let's add a folder called partials
to the folder views
. Let's further add a file called menu.eta
to the partials
-folder.
The structure of the project is now as follows.
tree --dirsfirst
.
├── views
│ ├── layouts
│ │ └── layout.eta
│ ├── partials
│ │ └── menu.eta
│ └── index.eta
└── app.js
Let's modify the menu so that it will contain three links. The links (and the content of the menu.eta
) will be as follows.
<nav>
<ul>
<li><a href="/">Index</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact us</a></li>
</ul>
</nav>
Partial files are included to view templates using the includeFile
function, which takes a path to a partial file as a parameter. The syntax for using the function is as follows; <%~ includeFile("path-to-partial"); %>
-- note that when using the function layout
, we do not use a tilde ~
, but with the includeFile
we do use one.
Let's add the menu partial to the layout.
<!DOCTYPE html>
<html>
<head>
<title><%= it.title %></title>
</head>
<body>
<%~ includeFile("./partials/menu.eta") %>
<%~ it.body %>
</body>
</html>
Now, when we make a request to our application, the response is as follows.
curl http://localhost:7777
<!DOCTYPE html>
<html>
<head>
<title>My Title</title>
</head>
<body>
<nav>
<ul>
<li><a href="/">Index</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact us</a></li>
</ul>
</nav>
<h1>My Heading</h1>
<p>The content!</p> </body>
</html>%
The application can also, based on the requested path, change the template that is being used. This is demonstrated in the next programming assignment.