Русский
Справочник функций Jet
Все функции, фильтры и конструкции, которые работают в своём Jet-шаблоне в _layouts/. Каждая проверена на той версии Jet, что стоит в trip2g (CloudyKit/jet v6.3.1), и на рендерере trip2g.
Страница из двух частей. Первая — полный справочник по самому Jet: встроенные функции, экранирование, конструкции языка. Вторая — указатель того, что добавляет trip2g (note, nvs, asset() и остальное), со ссылкой на страницу, где описан каждый пункт.
Если вы ещё не делали свой шаблон, начните с Шаблонов, а страницы собирайте из компонентов, как советует страница Компоненты шаблонов. Если что-то выводится не так — Отладка Jet-шаблонов.
На что похож Jet
Jet — движок шаблонов для Go. Синтаксис близок к text/template из Go, плюс то, что взято из Jinja2 и Twig: наследование шаблонов через extends и блоки, фильтры через |, тернарный оператор. Если вы знаете что-то из этого, читайте Jet как знакомый движок с отличиями ниже.
Как в шаблонах Go: скобки {{ … }}, if / else if / else / end, range по спискам и картам с веткой else для пустой коллекции, if с объявлением переменной.
Чем отличается от шаблонов Go:
text/template в Go |
Jet | |
|---|---|---|
| Переменные | $x := .Title |
x := note.Title(), без $ |
| Методы | .Title, вызывается сам |
note.Title(), скобки обязательны |
| Операторы | функции: eq, and, not |
==, &&, !, +, условие ? a : b |
if с объявлением |
if $x := f, проверяет $x |
if x := f(); условие, любое условие |
range с одной переменной |
даёт элемент | даёт индекс; пишите range i, x := |
Пайп a | f b |
a становится последним аргументом |
a становится первым аргументом |
| Повторяемый кусок | define + template "name" . |
block name(p="") + yield name(p=…) |
| Наследование шаблонов | extends нет |
extends; страница переопределяет блоки |
| Комментарии | {{/* … */}} |
{* … *} |
| Экранирование | html/template учитывает контекст |
одно и то же HTML-экранирование везде |
Как в Jinja2 и Twig: extends с блоками, которые страница переопределяет, import и include, фильтры с аргументами ("a-b" | replace: "-", " ", -1), условие ? a : b как в Twig. В отличие от них, все теги пишутся в {{ }} ({% %} нет), цикл — это range, а не for x in list, и блок принимает именованные параметры, как макрос в Jinja.
Экранирование не смотрит на контекст. Внутри <script> используйте json или safeJs, они описаны ниже.
Вывод по умолчанию экранируется
Это главное. {{ value }} экранирует HTML, поэтому заголовок вроде Q&A <черновик> попадёт на страницу текстом, а не тегами:
{{ "<b>bold</b>" }} {* → <b>bold</b> *}
{{ "<b>bold</b>" | unsafe }} {* → <b>bold</b> *}
Что из этого следует:
- Методы и поля, которые возвращают готовый HTML сервера, выводятся как есть, без фильтра:
note.HTMLString(),FirstListHTML(),FormSpecJSON(),SubgraphNamesJSON(),TitleHTML/ContentHTMLсекции,HTMLблока кода, функцииdefaultTemplate.*иasset(). | unsafeи| rawвыводят обычную строку без экранирования. Нужны они только для разметки, которую собирает сам шаблон или которая лежит в строке, и дляinjection.ContentHTML-инъекций сайта.- Для текста
| htmlбольше не нужен. Он экранирует один раз и помечает результат как безопасный, поэтому{{ title | html }}в старом шаблоне выводит то же, что{{ title }}, а не дважды экранированное&amp;. - Строковая функция или
+над безопасным выводом возвращают обычную строку, и она экранируется:{{ upper(note.HTMLString()) }}выведет теги текстом.
Фильтры сравниваются ниже, в разделе #Экранирование.
Встроенные функции Jet
| Функция | Возвращает |
|---|---|
len(x) |
Длину строки (в байтах), среза, массива или карты; число полей структуры |
isset(a, b, …) |
true, если все аргументы определены и не nil |
lower(s), upper(s) |
Строку в нижнем / верхнем регистре |
hasPrefix(s, prefix), hasSuffix(s, suffix) |
bool |
trimSpace(s) |
Строку без пробелов по краям |
repeat(s, n) |
s, повторённую n раз |
replace(s, old, new, n) |
s с заменой первых n совпадений; n = -1 — всех |
split(s, sep) |
[]string |
map(k1, v1, k2, v2, …) |
map[string]interface{} |
slice(a, b, …), array(a, b, …) |
[]interface{} |
ints(from, to) |
Последовательность from … to-1 для range |
json(v) |
JSON значения v, компактный, выводится без HTML-экранирования |
writeJson(v) |
Пишет JSON значения v в вывод, с переводом строки в конце |
includeIfExists(path, ctx?) |
Рендерит шаблон, если он есть; возвращает невидимый true/false |
exec(path, ctx?) |
Значение, которое другой файл шаблона отдал через return; его вывод отбрасывается |
dump(), dump("name") |
Текстовый дамп контекста, переменных и глобальных имён |
Фильтры экранирования (html, url, safeHtml, safeJs, raw, unsafe) — в отдельной таблице.
Три способа вызвать функцию
Функцию можно вызвать напрямую или через пайп. В пайпе значение слева становится первым аргументом, остальные пишутся после двоеточия:
{{ upper("hello") }} {* → HELLO *}
{{ "hello" | upper }} {* → HELLO *}
{{ "a-b-c" | replace: "-", " ", -1 }}
{* replace("a-b-c", "-", " ", -1) → a b c *}
{{ "Hello" | hasPrefix: "He" }} {* → true *}
{{ "a-b" | replace: "-", " ", -1 | upper }}
{* pipes chain → A B *}
{{ 3 | repeat: "ab" }} падает: это вызов repeat(3, "ab"). Пишите repeat("ab", 3).
Строки
{{ lower("ABC") }} {* → abc *}
{{ trimSpace(" x ") }} {* → x *}
{{ repeat("ab", 3) }} {* → ababab *}
{{ replace("aaa", "a", "b", 2) }} {* → bba *}
{{ range i, part := split("a,b,c", ",") }}
[{{ part }}]
{{ end }}
{* → [a] [b] [c] *}
{{ if hasSuffix(note.Permalink(), "/faq") }}
<a href="/faq/contact">Ask a question</a>
{{ end }}
len считает байты, а не буквы: len("héllo") — это 6, len("привет") — 12.
isset
{{ m := map("a", 1) }}
{{ isset(m["a"]) }} {* → true *}
{{ isset(m["z"]) }} {* → false: missing map key *}
{{ isset(undefinedVar) }} {* → false, no error *}
isset — единственное место, где неопределённая переменная не ошибка. В остальных случаях {{ undefinedVar }} останавливает рендер с identifier "undefinedVar" not available.
map, slice, ints
{{ links := map("docs", "/docs", "blog", "/blog") }}
{{ links["blog"] }} {{ links.blog }} {* both → /blog *}
{{ range i, n := ints(1, 4) }}
{{ n }}
{{ end }}
{* → 1 2 3 *}
- Ключи
map— строки, аргументов должно быть чётное число. ints(from, to)не включаетto, иfromдолжен быть меньшеto.ints(3, 1)— ошибка рендера.- По числу
rangeне ходит:{{ range i := 3 }}падает. Пишитеints(0, 3).
Индекс и срез
list[from:to] берёт элементы с from до to, не включая to. Любую границу можно опустить, и обе могут быть переменными или выражениями:
{{ colors := slice("red", "green", "blue", "black") }}
{{ colors[1] }} {* → green *}
{{ colors[1:3] }} {* → [green blue] *}
{{ from := 1 }}
{{ to := len(colors) - 1 }}
{{ colors[from:to] }} {* → [green blue] *}
{{ colors[from:] }} {* → [green blue black] *}
{{ colors[:to] }} {* → [red green blue] *}
Строки режутся так же, по байтам: {{ word := "hello" }}{{ word[1:4] }} выведет ell. Строковый литерал напрямую не режется: "hello"[1:4] не загрузится.
Ставьте пробелы вокруг - в индексе: colors[len(colors) - 2:] работает, а colors[len(colors)-2:] не загрузится — Jet прочитает -2 как число.
json и writeJson
<script>
window.pageData = {{ json(map(
"title", note.Title(),
"tags", note.Tags()
)) }};
</script>
<script type="application/json" id="data">
{{ writeJson(note.M().Raw()) }}
</script>
Обе экранируют <, > и & как <…, поэтому результат безопасен внутри <script>, и экранирование страницы его не трогает. writeJson добавляет перевод строки в конце, json — нет.
Тег может занимать несколько строк внутри скобок, как вызов json(map(…)) выше: длинный список аргументов переносите после запятой. Цепочку методов перед . перенести нельзя. Длинную цепочку разбейте на переменные (см. пример).
includeIfExists и exec
{{ includeIfExists("partials/banner") }}
{{ if !includeIfExists("partials/sidebar", note) }}
<p>No sidebar</p>
{{ end }}
includeIfExists рендерит шаблон, если он существует, и возвращает булево значение, которое ничего не печатает. Второй аргумент, если он есть, становится . внутри вставленного шаблона.
exec("lib/featured", данные) выполняет другой файл шаблона, отбрасывает всё, что он напечатал, и возвращает значение его {{ return }}. Внутри файла данные доступны как ., а note, nvs и другие переменные страницы работают. Путь отсчитывается от _layouts/ и пишется без расширения: exec("lib/featured") работает, exec("lib/featured.html") падает с template /lib/featured.html could not be found. exec — для данных (список, словарь, число), для разметки — компоненты. Примеры и правила путей — в Синтаксисе Jet: exec и return.
dump
<pre>{{ dump() }}</pre> {* контекст, переменные, глобальные, блоки *}
<pre>{{ dump("x") }}</pre> {* одна переменная: "x:=5 // float64" *}
dump показывает имена. Чтобы увидеть тип, значение и методы объекта, используйте debug() из trip2g.
Экранирование
| Фильтр | Что делает | Для чего |
|---|---|---|
html |
Функция: один раз экранирует <, >, &, ', " и возвращает безопасное значение |
Показать HTML-код текстом; сохранить экранированное значение в переменную |
safeHtml |
То же экранирование, пишет прямо в вывод | Ничего такого, чего не делает {{ value }} |
url |
Функция: экранирование для query (a b&c → a+b%26c), возвращает безопасное значение |
Одно значение параметра: ?q={{ term | url }} |
safeJs |
Экранирование JS-строки (' → \', < → <) |
Значение внутри строкового литерала JS |
raw / unsafe |
Без экранирования, пишет прямо в вывод | Разметка, которую собирает сам шаблон или которая лежит в обычной строке |
{{ s := "<b>Tom & Jerry</b>" }}
{{ s }} {* → <b>Tom & Jerry</b> *}
{{ s | html }} {* the same *}
{{ s | safeHtml }} {* the same *}
{{ s | unsafe }} {* → <b>Tom & Jerry</b> *}
{{ s | raw }} {* the same as unsafe *}
html или safeHtml. Для текста — ни тот, ни другой: {{ s }} уже экранирует. Разница в том, что они такое. html — функция, она возвращает значение, поэтому работает везде, где работает выражение, и результат второй раз не экранируется: {{ e := html(s) }} можно вывести позже, в тексте или в атрибуте. safeHtml, safeJs, raw и unsafe — писатели: они работают только последним фильтром в теге вывода. {{ t := s | unsafe }} не загрузится, а safeHtml(s) упадёт при рендере. Единственная работа, которая осталась у html, — показать разметку текстом: {{ note.HTMLString() }} рендерит заметку, а {{ html(note.HTMLString()) }} печатает её теги, чтобы читатель их увидел.
{{ e := html(s) }}
<p title="{{ e }}">{{ e }}</p>
{* both escaped once: <b>Tom & Jerry</b> *}
<pre>{{ html(note.HTMLString()) }}</pre>
{* the note's HTML as visible tags *}
url экранирует одно значение параметра. Он экранирует и /, :, &, поэтому не годится для целого адреса. Как и html, это функция, и её результат второй раз не экранируется:
{{ term := "a b&c/d" }}
<a href="/search?q={{ term | url }}">Search</a>
{* → href="/search?q=a+b%26c%2Fd" *}
raw и unsafe — один и тот же фильтр. Используйте его только для разметки, которой доверяете: собственные строки шаблона, injection.Content. Никогда — для текста, который написал автор или посетитель.
safeJs экранирует значение для строкового литерала JavaScript:
<script>
var title = '{{ note.Title() | safeJs }}';
</script>
Конструкции языка
Вывод, комментарии, пробелы
{{ note.Title() }} {* print an expression *}
{* a comment, never rendered *}
<li> {{- note.Title() -}} </li>
{* {{- and -}} trim whitespace on that side *}
Методы вызывайте со скобками. {{ note.Title }} без () выведет адрес функции, а не заголовок.
Переменные: := и =
{{ count := 0 }} {* объявить *}
{{ count = count + 1 }} {* присвоить существующей *}
=для переменной, которую не объявили, — ошибка рендера:could not assign "x" … variable "x" is uninitialised.:=внутриifилиrangeобъявляет новую переменную, которая до{{ end }}закрывает внешнюю. Чтобы изменить внешнюю, пишите=:
{{ x := 1 }}{{ if true }}{{ x := 2 }}{{ end }}{{ x }} {* → 1 *}
{{ x := 1 }}{{ if true }}{{ x = 2 }}{{ end }}{{ x }} {* → 2 *}
Операторы и значения
| Вид | Операторы |
|---|---|
| Арифметика | + - * / % |
| Сравнение | == != < > <= >= |
| Логика | && || ! |
| Тернарный | условие ? a : b |
| Индекс / срез | m["key"], m.key, list[0], list[1:3], list[from:to], str[1:3] |
- Числовые литералы — дробные:
{{ 7 / 2 }}выводит3.5. +со строкой склеивает:{{ "страница " + 2 }}→страница 2."",0,falseиnilвifи?:— ложь:{{ s ? s : "нет" }}. Пустой список — истина, поэтому списки проверяйте черезlen(list) > 0.||возвращает булево значение, а не первое непустое. Для значения по умолчанию есть coalesce() из trip2g.
if
{{ if n == 1 }}
one
{{ else if n == 2 }}
two
{{ else }}
many
{{ end }}
{{ if v, ok := links["blog"]; ok }}
<a href="{{ v }}">Blog</a>
{{ end }}
range
{{ range i, tag := note.Tags() }}
<span>{{ tag }}</span>
{{ end }}
{{ range key, value := links }}
{{ key }} → {{ value }}
{{ end }}
{{ range i, post := posts }}
<a href="{{ post.Permalink() }}">{{ post.Title() }}</a>
{{ else }}
<p>Nothing here.</p>
{{ end }}
- С одной переменной
rangeотдаёт индекс, а не значение.{{ range tag := note.Tags() }}даст0, 1, 2…. Всегда пишитеrange i, value :=. {{ else }}выводится, если коллекция пуста.- Ненужную переменную назовите
_:range _, tag := note.Tags().
block и yield
{{ block card(title="", url="") }}
<a class="card" href="{{ url }}">{{ title }}</a>
{{ end }}
{{ yield card(title="Документация", url="/docs") }}
{{ yield card(url="/blog", title="Блог") }}
- Аргументы передаются по имени. Позиционный аргумент —
yield card("Документация")— игнорируется, параметр остаётся со значением по умолчанию. - Задавайте значение по умолчанию каждому параметру. Блок, объявленный в самой странице, ещё и выводится там, где объявлен, без аргументов. Параметр без значения по умолчанию тогда падает с
missing name for block parameter. content— зарезервированное слово, параметр так называть нельзя.
Блок может оборачивать содержимое. Блок выводит то, что передал вызывающий, через {{ yield content }}:
{{ block panel(title="") }}
<section>
<h2>{{ title }}</h2>
{{ yield content }}
</section>
{{ end }}
{{ yield panel(title="Related") content }}
<p>Anything here becomes the panel body.</p>
{{ end }}
Значение после вызова становится . внутри блока: {{ yield menu() items }}.
import, include, extends
{{ import "blocks" }}
{* load the blocks of _layouts/blocks.html, render nothing *}
{{ include "partials/footer" }}
{* render another template here *}
{{ include "partials/card" note }}
{* … with `note` as `.` inside it *}
extends наследует базовый шаблон и заполняет его блоки:
{{ extends "base" }}
{{ block main() }}
<p>This replaces the main block of base.html</p>
{{ end }}
- Пути — относительно
_layouts/, без.html. extendsдолжен быть первым тегом файла, за ним —import, если они есть.- Как база и страница складываются вместе, с проверенным примером, — в Шаблонах: наследование.
- Кроме того, trip2g сам импортирует блоки, когда страница вызывает блок из другого файла шаблона. Шаблон, до которого дошли через
includeилиextends, этого не получает и импортирует компоненты сам через{{ import }}. См. Компоненты шаблонов: автоимпорт.
return и try / catch
{* lib/featured.html: the value exec gets *}
{{ return nvs.ByGlob(. + "/*.md").Public().Limit(3).All() }}
{{ try }}
{{ stats := exec("lib/stats", "blog") }}
<p>{{ stats.count }} posts</p>
{{ catch err }}
{{ if currentUser.IsAdmin() }}
<p>{{ err.Error() }}</p>
{{ end }}
{{ end }}
returnотдаёт значениеexec. В обычной странице он ничего не останавливает и ничего не выводит.tryрендерит своё тело; если внутри что-то упало, вывод тела выбрасывается и рендеритсяcatch. Безcatchупавшийtryничего не выводит. Шаблон, который не загрузился,tryне ловит.
Примеры — в exec и return и try / catch.
Что добавляет trip2g
trip2g регистрирует восемь глобальных функций, передаёт каждому своему шаблону набор переменных и подставляет два плейсхолдера в исходник шаблона. У каждого пункта ниже — ссылка на страницу, где он описан. То, что нигде больше не описано, коротко описано здесь.
Глобальные функции
| Функция | Возвращает | Где описана |
|---|---|---|
asset("file.css") |
URL файла рядом с шаблоном; если такого файла нет — аргумент как есть | yield_blocks, Шаблоны |
debug(expr) |
Go-тип, значение и список методов expr |
Отладка Jet-шаблонов |
yield_blocks("prefix") |
Выводит все блоки, чьё имя начинается с префикса (или подходит под /regex/) |
yield_blocks |
coalesce(a, b, …) |
Первый аргумент, который задан и не пуст; иначе последний | Ниже |
arg_type("param", "type", "comment") |
Ничего; описывает параметр блока для инструментов | Ниже |
parseJSON(s), parseYAML(s), parseCSV(s) |
Текст в виде словарей и списков (CSV — список строк таблицы); nil на неверных данных |
Шаблоны: разбор данных |
coalesce считает пустыми отсутствующий ключ карты, nil, "", пустой список и пустую карту. 0 и false — значения:
{{ videos := map("en", "intro-en.mp4") }}
{{ coalesce(videos[note.Lang()], videos["en"]) }}
{{ coalesce(
note.M().GetString("subtitle", ""),
note.Description(),
note.Title()
) }}
arg_type ничего не выводит. trip2g читает его при загрузке и сообщает инструментам, которые показывают список блоков, тип и описание параметра:
{{ block hero(title="", image="") }}
{{ arg_type("title", "string", "Текст заголовка") }}
{{ arg_type("image", "asset", "Фоновая картинка") }}
…
{{ end }}
Переменные в каждом своём шаблоне
| Переменная | Что это | Где описана |
|---|---|---|
note |
Текущая страница | Шаблоны, полный список методов — Шаблоны: API |
nvs |
Все заметки сайта: поиск и выборки | Шаблоны: API |
title |
Заголовок страницы после шаблона заголовка сайта — готов для <title> |
Ниже |
publicURL |
Адрес основного домена, например https://example.com |
Шаблоны: SEO-теги |
htmlInjectionsHead, htmlInjectionsBodyEnd |
Фрагменты, которые админ добавил в Админке → SEO & URLs → HTML Injections; выводите injection.Content |
Шаблоны: HTML-инъекции |
defaultTemplate.Header(), .Footer() |
HTML шапки / подвала дефолтного шаблона; "", если у страницы их нет |
Ниже |
defaultTemplate.Styles() |
Теги <link> со стилями дефолтного шаблона |
Ниже |
defaultTemplate.UserSpaceScripts() |
<script> с настройками и скрипты, нужные виджетам на клиенте |
Ниже |
currentUser.IsAdmin() |
true, если страницу смотрит админ сайта |
Ниже; используется в Темах |
Чтобы обернуть свой контент в шапку, подвал и стили дефолтного шаблона:
<head>
<title>{{ title }}</title>
{{ defaultTemplate.Styles() }}
{{ defaultTemplate.UserSpaceScripts() }}
</head>
<body>
{{ defaultTemplate.Header() }}
<main>{{ note.HTMLString() }}</main>
{{ defaultTemplate.Footer() }}
{{ if currentUser.IsAdmin() }}
<a href="/admin">Admin</a>
{{ end }}
</body>
UserSpaceScripts() пишет объект настроек только при первом вызове, второй вызов его не дублирует. В превью шаблона (/_system/renderlayout) все четыре функции defaultTemplate возвращают "", а currentUser.IsAdmin() — false.
Плейсхолдеры в исходнике шаблона
@lid и @did в файле шаблона до разбора заменяются на id файла (mesh/bar.html → mesh_bar / mesh-bar, my-theme/card.html → my_theme_card / my-theme-card), чтобы имена блоков и CSS-классов не пересекались. См. yield_blocks и BEM-именование.
Методы note, note.M(), nvs и выборок
Полный список — в Шаблонах: API: методы заметки (Title, HTMLString, Tags, Lang, LangAlternativesList, HasCodeLanguage…), доступ к frontmatter (GetString, GetInt, GetBool, GetStrings…), nvs (ByPath, ByPermalink, ByWikilink, BackLinks…), выборки (ByGlob, SortBy, SortByMeta, Public, Limit…) и PartialRenderer. Там же — что выборка возвращает без фильтров и как ведёт себя сортировка. FormSpecJSON() описан в Формах, debug() и note.M().Debug() — в Отладке.
PermalinkEncoded() — это Permalink(), в котором каждый сегмент пути закодирован процентами: /привет мир → /%D0%BF…%20%D0%BC…. Это кодирование URL, а не HTML-экранирование. Раз вывод экранируется, Permalink() тоже безопасен в href. PermalinkEncoded() нужен, когда в пути есть не-ASCII символы или пробелы и нужен строго закодированный адрес: в sitemap, ленте или ссылке, которую читает другая программа.
Языковой переключатель:
{{ range i, alt := note.LangAlternativesList() }}
<a href="{{ alt.PermalinkEncoded() }}" hreflang="{{ alt.Lang() }}">
{{ alt.LangName() }}
</a>
{{ end }}
Две ловушки стоит знать заранее:
- Поиск, который ничего не нашёл (
ByPath,ByPermalink,ByWikilink,First()/Last()выборки,LangAlternative(),Section(),FirstList()), возвращаетnil. Работают и{{ if x }}, и{{ if x == nil }}, аx.Title()на нём останавливает рендер. Удобнее всего объявить и проверить в одном теге, как в примере ниже. nvs.ByGlob(...)без.Public()возвращает и платные, и закрытые, и системные (_) заметки.
{{ if about := nvs.ByPermalink("/about"); about }}
<a href="{{ about.PermalinkEncoded() }}">{{ about.Title() }}</a>
{{ end }}
Пример: последние посты со счётчиком
Список, который берёт пять самых новых публичных заметок из blog/, сортирует по полю date из frontmatter и показывает, сколько их всего:
{{ query := nvs.ByGlob("blog/*.md").Public() }}
{{ total := len(query.All()) }}
{{ sorted := query.SortByMeta("date").Desc().SortBy("Title") }}
{{ posts := sorted.Limit(5).All() }}
<h2>Latest posts</h2>
<p>Showing {{ len(posts) }} of {{ total }}</p>
<ul>
{{ range i, post := posts }}
<li>
<a href="{{ post.PermalinkEncoded() }}">{{ post.Title() }}</a>
<time>{{ post.M().GetString("date", "undated") }}</time>
</li>
{{ else }}
<li>No posts yet.</li>
{{ end }}
</ul>
Как это работает:
.Public()отбрасывает платные заметки и черновики (_), поэтомуtotalсчитает только то, что посетитель может открыть.query.All()выборку не меняет, поэтому тот жеqueryиспользуется ещё раз для сортировки. Изменил бы её толькоFirst().- Сортировка лежит в своей переменной
sorted, потому что цепочку методов нельзя продолжить на следующей строке. date: 2024-05-10приходит строкой, а даты в формате ISO сортируются как строки правильно. Заметки безdateуходят в конец..SortBy("Title")упорядочивает посты с одинаковой датой. Без него их порядок менялся бы от рендера к рендеру.- Заголовок вроде
Q&Aэкранируется при выводе, фильтр не нужен.
Этот шаблон рендерит тест в репозитории trip2g (internal/layoutloader/jet_functions_example_test.go), поэтому пример не устареет незаметно. Короткие примеры на этой странице рендерит internal/layoutloader/jet_functions_snippets_test.go. Код примеров одинаков на русской и английской странице, поэтому текст в них английский: тест сверяет обе страницы.
Смотрите также
- Шаблоны — как устроены шаблоны
- Компоненты шаблонов — как устроить шаблон
- Приложение поверх trip2g — шаблон, который отдаёт JavaScript-приложение
- Шаблоны: API — методы
note,nvs, выборок и frontmatter - Синтаксис Jet — обзор синтаксиса
- Отладка Jet-шаблонов
- yield_blocks — компоненты, ассеты,
@lid/@did - Превью лейаута — проверить шаблон без загрузки
- Справочник синтаксиса Jet — документация автора движка