Document Template
When developing podlets which are to be composed together with other podlets into a full HTML page by a layout it is important that the development of the podlet happens under the same constraints when developing in isolation as when running inside a full layout.
One example of such a constraint might be when a given CSS class is set on the
<body> tag and used to set certain CSS restrictions on the whole document. If
this class is then not present when developing a podlet, that podlet might end
up looking different in isolation from when its included in a layout.
To cater for this, Podium has a concept of a document template that is intended to be used in both layout servers when serving a full pages and in podlets when in development.
A document template is typically an HTML wireframe where layout and podlet
content is placed into the <body> section of the document:
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
</head>
<body>
<!-- layout / podlet content goes here -->
</body>
</html>
Rendering
A document template is used by calling the .render() methods in the podlet
and layout modules or the res.podiumSend() provided by
whichever HTTP framework is being used.
- Express
- Fastify
- HTTP
app.get(layout.pathname(), (req, res) => {
const incoming = res.locals.podium;
const document = podlet.render(incoming, html`<div>content to render</div>`);
res.send(document);
});
app.get(layout.pathname(), (req, res) => {
const incoming = reply.app.podium;
const document = podlet.render(incoming, html`<div>content to render</div>`);
reply.send(document);
});
const server = http.createServer(async (req, res) => {
const incoming = new HttpIncoming(req, res);
const document = layout.render(incoming, html`<div>content to render</div>`);
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html');
res.end(document);
});
Customizing
Podium ships with a default document template which should cover most use cases. It is possible, however, to set a custom document template which can then be plugged into both layout and podlet servers.
A custom document template is set by using the .view() method in the
podlet and layout modules.
layout.view((incoming, body, head) => `<!doctype html>
<html lang="${incoming.context.locale}">
<head>
<meta charset="${incoming.view.encoding}">
<title>${incoming.view.title}</title>
${head}
</head>
<body>
${body}
</body>
</html>`;
);
Request Properties
A document template will need properties which are request bound. This can be
any type of property, but the value of the <title> element is one such
example.
It is possible to pass on properties to the document template by using the
.view property on HttpIncoming.
- Express
- Fastify
- HTTP
app.get(layout.pathname(), (req, res) => {
const incoming = res.locals.podium;
incoming.view = {
title: `My Site / ${someRequestValue}`,
};
const document = layout.render(incoming, html`<div>content to render</div>`);
res.send(document);
});
app.get(layout.pathname(), (req, res) => {
const incoming = reply.app.podium;
incoming.view = {
title: `My Site / ${someRequestValue}`,
};
const document = layout.render(incoming, html`<div>content to render</div>`);
reply.send(document);
});
const server = http.createServer(async (req, res) => {
const incoming = new HttpIncoming(req, res);
incoming.view = {
title: `My Site / ${someRequestValue}`,
};
const document = layout.render(incoming, html`<div>content to render</div>`);
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html');
res.end(document);
});
Assets
On the HttpIncoming object which is passed on to the document
template one can find an array of AssetCSS objects on the .css property and
an array of AssetJS objects on the .js property . These properties hold the
assets of a podlet or a layout. In a layout they can hold the assets of the
requested podlets in addition to the assets of the layout itself.
Please see the asset documentation for more information.
The arrays of AssetCSS and AssetJS objects can easily be converted into
HTML in a document template by running each object through the .buildLinkElement()
or .buildScriptElement()
methods found in the @podium/utils package:
import utils from '@podium/utils';
[ ... ]
layout.view((incoming, body, head) => `<!doctype html>
<html lang="${incoming.context.locale}">
<head>
<meta charset="${incoming.view.encoding}">
${incoming.css.map(utils.buildLinkElement).join('\n')}
${incoming.js.map(utils.buildScriptElement).join('\n')}
<title>${incoming.view.title}</title>
${head}
</head>
<body>
${body}
</body>
</html>`;
);
template(HttpIncoming, fragment, [args])
A document template is implemented using a plain JavaScript function that returns a string.
The document template accepts, and will be called with, the following arguments:
HttpIncoming (required)
An instance of the HttpIncoming class.
fragment
A string that is intended to be a used as a fragment in the final HTML document.
[args]
All following arguments given to the .render() or res.podiumSend() methods
in the podlet and layout modules will be
passed on to the document template.
The following is an example of how such additional arguments might be used to pass on parts of a page to the document template.
- Express
- Fastify
- HTTP
layout.view = (incoming, body, head) => {
return `
<html>
<head>${head}</head>
<body>${body}</body>
</html>
`;
};
app.get(layout.pathname(), (req, res) => {
const incoming = res.locals.podium;
const head = html`<meta ..... />`;
const body = html`<section>my content</section>`;
const document = layout.render(incoming, body, head);
res.send(document);
});
layout.view = (incoming, body, head) => {
return `
<html>
<head>${head}</head>
<body>${body}</body>
</html>
`;
};
app.get(layout.pathname(), (req, res) => {
const incoming = reply.app.podium;
const head = html`<meta ..... />`;
const body = html`<section>my content</section>`;
const document = layout.render(incoming, body, head);
reply.send(document);
});
layout.view = (incoming, body, head) => {
return `
<html>
<head>${head}</head>
<body>${body}</body>
</html>
`;
};
const server = http.createServer(async (req, res) => {
const incoming = new HttpIncoming(req, res);
const head = html`<meta ..... />`;
const body = html`<section>my content</section>`;
const document = layout.render(incoming, body, head);
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html');
res.end(document);
});