Skip to content

Instantly share code, notes, and snippets.

@line-o
Last active June 19, 2023 14:53
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save line-o/d2682a42a3028d5b4125759a10603c5f to your computer and use it in GitHub Desktop.
Save line-o/d2682a42a3028d5b4125759a10603c5f to your computer and use it in GitHub Desktop.
Prerender HTML with eXist-db's templating library

pre-rendering HTML with eXist-db's Templating library

  • put all files in a collection /db/apps/pre-render
  • open /db/apps/pre-render/pre-render.xq in eXide
  • evaluate
  • check /db/apps/pre-render for rendered news articles
<main class="news-list__latest">
<ul>
<li data-template="templates:each" data-template-from="articles" data-template-to="article">
<a data-template="pr:article-link"/>
</li>
</ul>
</main>
<main class="news-detail">
<article data-template="pr:include-article" />
<a href="latest-news-rendered.html">back to list</a>
</main>
<news>
<article id="a5" date="2022-12-13">
<header>Latest news</header>
<p>Are so important...</p>
</article>
<article id="a4" date="2022-03-04">
<header>Lorem of the Rings</header>
<p>Lorem ipsum dolor sit amet...</p>
</article>
<article id="a3" date="2022-02-22">
<header>Latest news</header>
<p>Yada yada yada...</p>
</article>
<article id="a1" date="1970-01-01">
<header>Oldest news</header>
<p>In a galaxy far far away ...</p>
</article>
</news>
<html>
<head>
<title data-template="pr:title" />
</head>
<body>
<main data-template="pr:include-page-type" />
</body>
</html>
xquery version "3.1";
declare namespace pr="http://line-o.de/ns/pre-render";
import module namespace templates="http://exist-db.org/xquery/html-templating";
import module namespace lib="http://exist-db.org/xquery/html-templating/lib";
(:
: We have to provide a lookup function to templates:apply to help it
: find functions in the imported application modules. The templates
: module cannot see the application modules, but the inline function
: below does see them.
:)
declare variable $pr:lookup := function($functionName as xs:string, $arity as xs:int) {
function-lookup(xs:QName($functionName), $arity)
};
declare variable $pr:templates := map {
"news": "news.html",
"latest-news": "latest-news.html"
};
(: custom include function including the template set in the model :)
declare function pr:include-page-type ($node as node(), $model as map(*)) as node() {
let $template := $pr:templates($model?page-type)
return
if (empty($template))
then error((), "No template found for " || $model?page-type, $model)
else lib:include($node, $model, $template)
};
(: templating configuration :)
declare variable $pr:config := map {
$templates:CONFIG_APP_ROOT : "/db/apps/pre-render",
$templates:CONFIG_STOP_ON_ERROR : true(),
$templates:CONFIG_FILTER_ATTRIBUTES : false()
};
(: base template for all pages :)
declare variable $pr:page-template := doc("page.html");
(: our render function is only dependent on the model :)
declare function pr:render ($model as map(*)) {
templates:apply(
$pr:page-template, $pr:lookup,
$model,
$pr:config
)
};
declare
%templates:wrap
function pr:title ($node as node(), $model as map(*)) as xs:string {
$model?title
};
(:------ news specific --------:)
declare
%templates:replace
function pr:include-article ($node as node(), $model as map(*)) as node() {
$model?data
};
declare
%templates:wrap
function pr:article-link ($node as node(), $model as map(*)) as node()* {
attribute href {
"news-" || $model?article/@id/string() || ".html"
},
$model?article/header/text()
};
declare function pr:render-article ($article as node()) as xs:string? {
let $news-model := map {
'page-type': 'news',
'data' : $article,
'title' : "NEWS - " || $article/header/string()
}
let $rendered := pr:render($news-model)
let $filename := 'news-' || $article/@id/string() || '.html'
return
xmldb:store('/db/apps/pre-render', $filename, $rendered)
};
(:~
: render all given article elements and store the rendered HTML
:)
declare function pr:render-article-detail($articles as element(article)*) as xs:string* {
for-each($articles, pr:render-article#1)
};
declare function pr:sort-by-date ($article as element(article)) as xs:integer {
-xs:integer(replace($article/@date/string(), '-', ''))
};
(:~
: render all given article elements and store the rendered HTML
:)
declare function pr:render-latest-news($articles as element(article)*, $how-many as xs:integer) as xs:string* {
let $sorted := sort($articles, "?numeric=yes", pr:sort-by-date#1)
let $rendered := pr:render(map {
'page-type': 'latest-news',
'articles': $sorted[position() <= $how-many],
'title' : "BREAKING NEWS"
})
return
xmldb:store('/db/apps/pre-render', 'latest-news-rendered.html', $rendered)
};
(: load article data :)
let $articles := doc('news.xml')//article
(: render HTML and store it :)
return (
pr:render-article-detail($articles),
pr:render-latest-news($articles, 3)
(: Exercise left to the user :)
(: pr:render-news-per-year($articles) :)
)
@dizzzz
Copy link

dizzzz commented Nov 14, 2022

nice!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment