Files
plainleaf/libraries/Library/Std/APIs/Template.md
T
Matouš Jan FialkaandGitHub baeb688ddc Fix: template.each returns nil on empty results (#1899)
* Fix: `template.each` returns nil on empty results

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Fix typo

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

---------

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
2026-03-19 16:26:32 +01:00

114 lines
3.3 KiB
Markdown

---
description: APIs to create and render templates
tags: meta/api
---
APIs to create templates from strings or pages, and render query collections using templates.
# API
## template.new(templateStr, stripIndent)
Creates a template function from a string. The template string can contain `${expression}` placeholders that will be interpolated when the template is called. Conventionally, template strings typically use `[==[` and `]==]` as delimiters.
If `stripIndent` is `true` or omitted, leading 4-space indentation is stripped from each line.
Example:
```lua
-- the `templates` global table is available for custom templates
templates.greet = template.new [==[Hello ${name}!]==]
```
Which elsewhere can be used, either directly:
```lua
${templates.greet {name = "Pete"}}
```
or together with `template.each` (see below):
```lua
${template.each(query[[from tags.page limit 3]], templates.greet)}
```
Resulting in something along the lines of `Hello index!Hello CONFIG!...`
## template.each(collection, fn)
Iterates over a collection and applies a template to each element, concatenating the results. Commonly used to render query results.
Example:
```lua
${template.each(query[[from index.tag "page" limit 3]], template.new [==[
* [[${name}]]
]==])}
```
# Pre-built templates
The `templates` table contains ready-made templates for common query rendering patterns. These are defined in [[^Library/Std/Infrastructure/Query Templates]].
* **`templates.pageItem`** — renders a page as `* [[name]]`
* **`templates.fullPageItem`** — renders a page as `* [[name|name]]` (full path)
* **`templates.taskItem`** — renders a task as a togglable `* [state] [[ref]] name`
* **`templates.itemItem`** — renders an item as `* [[ref]] name`
* **`templates.paragraphItem`** — renders a paragraph as `* [[ref]] text`
* **`templates.tagItem`** — renders a tag as `* [[tag:name|#name]]`
# Implementation
```space-lua
-- priority: 50
-- Template API root table
template = template or {}
-- Template storage table
templates = {}
-- Iterates over a table/array and applies a function to each element,
-- concatenating the results
function template.each(tbl, fn)
local result = {}
for _, item in ipairs(tbl) do
table.insert(result, fn(item))
end
return #result > 0 and table.concat(result) or nil
end
-- Creates a new template function from a string template
function template.new(templateStr, stripIndent)
-- Preprocess: strip indentation
if stripIndent == nil or stripIndent == true then
local lines = {}
local splitLines = string.split(templateStr, "\n")
for _, line in ipairs(splitLines) do
line = string.gsub(line, "^ ", "")
table.insert(lines, line)
end
templateStr = table.concat(lines, "\n")
end
return function(obj)
return spacelua.interpolate(templateStr, obj)
end
end
-- Reads a template from a page
-- Return value:
-- * The template itself
-- * The extracted frontmatter
function template.fromPage(name, raw)
local fm = index.extractFrontmatter(space.readPage(name), {
removeFrontMatterSection = true,
removeTags = true
})
local templateText = string.trimStart(fm.text)
if raw then
-- Don't actually parse this as a template, just pass it along
return function()
return templateText
end, fm.frontmatter
end
return template.new(templateText, false), fm.frontmatter
end
```