Поля данных
Поля данных подставляют во фрейм значения из страницы и сайта: название страницы в заголовок, логотип в шапку, адрес главной в ссылку. Например, поле {{page.title}} выводит название страницы. Условия, повтор и вывод HTML вы ставите атрибутами data-op-* на сам тег, поэтому шаблон остается обычным HTML.
Один фрейм статьи показывает на каждой странице ее собственные заголовок, дату и превью. AI-агент правит текст в свойствах страницы или в данных и не трогает разметку.
Поля данных работают во фреймах и в тексте страницы. Какие поля читает фрейм конкретной страницы, показывает GET /pages/{ref}/data-fields. Полный синтаксис и все поля описывает справочник API.
Как выглядит шаблон
Ниже — фрейм, по которому OnPress выводит статью. Он показывает шапку сайта, заголовок и дату статьи, обложку, рейтинг, текст статьи, таблицу с командой и подвал. Вместо конкретного заголовка или даты в нем стоят поля, и на каждой странице OnPress подставляет туда значения этой страницы. А условная логика решает, какие части показать: например, рейтинг виден только у статей, где он есть.
<!-- frame/site__header /-->
<main class="article">
<h1>{{page.title || Заголовок страницы}}</h1>
<p class="article__meta">{{page.date}} · {{page.author_name || Редакция}}</p>
<img class="article__cover" src="{{page.thumbnail_url}}" alt="{{page.title}}" data-op-if="page.thumbnail_url">
<span class="rating" data-op-if="page.data.rating">{{page.data.rating}} / 5</span>
<span class="rating rating--empty" data-op-unless="page.data.rating">Без оценки</span>
<div class="article__text" data-op-html="page.content"></div>
<table class="team">
<tr data-op-repeat="team">
<td><img src="{{item.photo.url}}" alt="{{item.name}}"></td>
<td>{{item.name || Без имени}}</td>
<td><a href="/team/{{item.slug}}/">{{item.role || Сотрудник}}</a></td>
</tr>
</table>
</main>
<!-- frame/site__footer /-->
В шаблоне три вида записей. Поле в двойных фигурных скобках, например {{page.title}}, выводит значение. Атрибут data-op-… управляет тегом, на котором стоит: показывает его по условию, повторяет для каждого элемента набора или вставляет в него HTML. Строка <!-- frame/site__header /--> вставляет другой фрейм, например шапку или подвал сайта.
Значение
Запишите имя поля в двойных фигурных скобках там, где должно стоять значение: в тексте тега или в значении атрибута. Текст после || — заглушка. Ее показывают страница и превью в консоли, когда у поля нет значения. Пробелы вокруг || и внутри скобок по краям не важны. Например:
<h1>{{page.title || Заголовок страницы}}</h1>
<p>Автор: {{page.author_name || редакция}}</p>
На странице «Тарифы» с автором Анной Петровой получится:
<h1>Тарифы</h1>
<p>Автор: Анна Петрова</p>
Значение в атрибуте
Поле работает в любом атрибуте: href, src, alt, title, class, datetime, aria-label, data-что-угодно. Поле может занимать все значение атрибута или стоять внутри него вместе с текстом, и полей в одном значении бывает несколько. Например, ссылка с адресом по слагу и классом по типу статьи:
<a class="card card--{{item.type || article}}" href="/blog/{{item.slug}}/">
{{item.title}}
</a>
Адреса в href, src и других атрибутах-ссылках OnPress проверяет так же, как ссылки в тексте страницы, поэтому адрес вида javascript: из данных на страницу не попадет. В обработчиках событий onclick и других on… поле не работает: скобки остаются текстом.
Вычисления и формат
Фильтр меняет значение перед выводом: считает цену с наценкой, пишет дату словами, обрезает длинный заголовок. Запишите фильтр после имени поля через вертикальную черту |, а его параметры — после двоеточия через запятую. Фильтров может быть несколько, и каждый получает результат предыдущего. Например, цена с НДС с пробелами между разрядами и дата публикации:
<p class="price">{{item.price | times: 1.2 | number_format: 0, ",", " "}} ₽</p>
<time>{{page.date_iso | date: "%-d %B %Y"}}</time>
Для товара с ценой 12500 на русской странице от 30 октября 2026 года получится:
<p class="price">15 000 ₽</p>
<time>30 октября 2026</time>
Если у поля нет значения, фильтры его пропускают и OnPress показывает заглушку после ||. Строку в значении атрибута пишите в одинарных кавычках, потому что сам атрибут стоит в двойных: title="{{item.name | append: ' — автор'}}". Основные фильтры:
| Фильтр | Что делает | Пример |
|---|---|---|
plus, minus, times, divided_by | складывает, вычитает, умножает, делит | times: 1.2: 1000 → 1200 |
round, ceil, floor | округляет до знаков после запятой, вверх, вниз | round: 1: 2.675 → 2.7 |
number_format | число с разрядами и нужным числом знаков | number_format: 2, ",", " ": 1234567.891 → 1 234 567,89 |
date | дата в своем формате, месяцы на языке сайта | date: "%d.%m.%Y": 2026-10-30 → 30.10.2026 |
upcase, downcase, capitalize | меняет регистр | capitalize: москва → Москва |
truncate, truncatewords | обрезает до числа символов или слов | truncate: 8, "…": Длинный заголовок → Длинный… |
append, prepend, replace, remove | дописывает, заменяет, убирает текст | replace: "-", " ": a-b-c → a b c |
strip, strip_html | убирает пробелы по краям, теги | strip_html: <b>x</b> → x |
default | заменяет пустое значение | default: "Без названия" |
size, first, last, join, map, where, sort | работают со списками | join: ", ": [a, b] → a, b |
Синтаксис фильтров повторяет шаблонизатор Liquid. Все фильтры с параметрами, коды даты и ошибки описывает справочник.
Условная логика
Условная логика показывает или прячет часть шаблона в зависимости от данных. Например, рейтинг виден только у статей, где он указан. Для этого поставьте на тег атрибут data-op-if с именем поля: если у поля есть значение, тег остается на странице, если нет — OnPress убирает тег вместе со всем, что внутри. data-op-unless работает наоборот и выводит тег, только когда значения нет. Пара таких тегов дает «если — иначе»:
<span class="rating" data-op-if="page.data.rating">{{page.data.rating}} / 5</span>
<span class="rating rating--empty" data-op-unless="page.data.rating">Без оценки</span>
У статьи с rating: 4 на странице будет только «4 / 5», у статьи без рейтинга — только «Без оценки».
Значения нет, когда поле отсутствует, пустое, равно 0 или false, подробно — в разделе «Пустое значение».
Условие может и сравнивать значение с числом или строкой. Например, плашка «Выбор редакции» видна только у статей с рейтингом выше 4, а метка «Новость» — только у карточек типа news:
<span class="badge" data-op-if="page.data.rating > 4">Выбор редакции</span>
<span class="tag" data-op-if="item.type == 'news'">Новость</span>
Для сравнения есть ==, !=, >, <, >= и <=, а contains проверяет, есть ли слово в строке или элемент в списке: data-op-if="item.tags contains 'акция'". Несколько условий объединяют and и or, not переворачивает условие, а скобки задают порядок: data-op-if="page.data.price >= 1000 and not page.data.sold". Числа OnPress сравнивает как числа, а не как текст. Строку в условии пишите в одинарных кавычках. Все правила сравнения описывает справочник.
Условие можно поставить на любой тег, в том числе на целый раздел: <figure data-op-if="page.thumbnail_url"> спрячет обложку вместе с подписью у статьи без превью. Если на одном теге стоят и data-op-if, и data-op-unless, тег остается, только когда выполнены оба условия. Внутри повтора условие проверяет поле своего элемента, например data-op-if="item.photo.url" уберет фото только у карточек без фото.
Повтор
data-op-repeat="набор" повторяет тег по разу на каждый элемент набора. Внутри поля item.* выводят значения своего элемента. Например, строки таблицы из набора team:
<table>
<tr data-op-repeat="team">
<td>{{item.name}}</td>
<td>{{item.role}}</td>
</tr>
</table>
Чтобы выбрать часть списка, запишите в атрибуте JSON с параметрами: limit — сколько элементов, offset — сколько пропустить, match — отбор по полю, sort и order — порядок дочерних страниц pages.children. Например, три последние новости:
<article class="news-card" data-op-repeat='{"source":"news","limit":3}'>
<a href="{{item.url}}">{{item.title}}</a>
</article>
Отбор match оставляет элементы, у которых поле совпадает с полем страницы: {"source":"faq","match":{"page":"page.slug"}} выведет только вопросы текущей страницы из общего набора faq.
Чтобы повторить несколько тегов подряд без общей обертки, положите их в <template data-op-repeat="…">. На странице останутся только повторенные теги, без <template>. Все источники, параметры, пустой список и вложенный повтор описывает справочник «Повторитель».
Связи между наборами
Связь выводит запись из другого набора по ключу из свойств страницы или из поля элемента. Например, у статьи в свойствах стоит слаг автора author: anna, а имя и фото автора лежат в наборе authors. Поставьте поле с ключом в квадратные скобки после имени набора, и OnPress выведет запись набора с таким slug:
<p class="byline">{{data.authors[page.data.author].name || Редакция}}</p>
Для статьи с author: anna и записью {"slug": "anna", "name": "Анна Петрова"} в наборе authors получится <p class="byline">Анна Петрова</p>, а у статьи без автора — «Редакция».
Если записи с таким slug нет, OnPress ищет запись с таким id. Ключ может прийти из элемента повтора: {{data.authors[item.author].name}} выводит автора у каждой карточки в списке статей. Условие тоже работает со связью: data-op-if="data.authors[page.data.author]" прячет блок автора, если в наборе нет такой записи. Когда вы меняете набор authors, OnPress пересобирает все страницы, которые выводят его записи. Как найти запись по другому полю, описывает справочник.
HTML и markdown
data-op-html="поле" вставляет в тег значение как HTML. Так шаблон выводит текст страницы, поле page.content:
<div class="article__text" data-op-html="page.content"></div>
data-op-md="поле" разбирает в значении строчный markdown: **жирный**, *курсив*, [ссылка](/адрес/) и код в обратных кавычках. Остальной HTML в значении он экранирует. Если у поля нет значения, в теге остается его исходное содержимое, например «Текст скоро появится». В data-op-html берите только свои данные: OnPress вставляет их без изменений.
Фрейм, который вставляют парной вставкой с содержимым, выводит это содержимое полем frame.content:
<aside class="callout callout--{{frame.type || info}}" data-op-html="frame.content"></aside>
Иллюстрации
data-op-picture="поле" на <img> превращает тег в <picture> с WebP и вариантами 1x и 2x, как у иллюстрации в тексте страницы. Классы, alt, loading, width и height OnPress берет с тега. Если иллюстрации нет в медиатеке сайта, OnPress ставит ее адрес в src того же <img>. Например, превью карточки:
<img class="card__img" loading="lazy" alt="{{item.title}}" data-op-picture="item.image.url">
Поля в тексте страницы
Поля работают и в markdown статьи: в абзаце, заголовке, тексте и адресе ссылки. HTML-блок с атрибутами data-op-* тоже работает, если стоит на отдельных строках. Выгрузка GET /pages/{ref}/markdown отдает текст со скобками в том виде, в каком вы его записали. Например:
## Как устроен {{site.name || наш сайт}}
Вы читаете [{{page.title || эту статью}}]({{page.url}}).
Скобки текстом
Значение подставляет только имя поля, например page.title, или имя поля с фильтрами. {{ name }}, {{ count + 1 }} и {{ message | capitalize }} остаются текстом. Скобки внутри <script>, <style>, <code>, <pre>, <textarea>, в блоках кода markdown и в HTML-комментариях OnPress тоже не трогает. Шаблон Vue или Alpine поставьте в тег с атрибутом data-op-raw, тогда скобки внутри останутся как есть. Чтобы показать скобки текстом в любом другом месте, поставьте перед ними обратную косую: \{{page.title}} страница выведет как {{page.title}}. Например:
<div id="app" data-op-raw>
<p>{{ user.name }}</p>
</div>
Пустое значение
Пустыми OnPress считает отсутствующее поле, пустую строку, 0, false, null и пустой список, как функция empty() в PHP. Строка из пробелов — значение. Для пустого поля OnPress показывает заглушку, а без заглушки ставит пустую строку. data-op-if снимает тег, а data-op-unless оставляет. Все случаи показывает справочник.
Какие поля есть
Имя поля начинается с того, откуда берется значение. Чаще всего нужны такие поля:
| Поле | Что выводит |
|---|---|
site.name, site.logo_url, site.home_url | название, логотип и адрес главной сайта |
page.title, page.url, page.date | заголовок, адрес и дату страницы |
page.content | текст страницы, выводите его через data-op-html |
page.thumbnail_url, page.author_name | превью и автора страницы |
page.data.<ключ> | свое значение из свойств страницы, например page.data.rating |
item.<поле> | поле записи внутри data-op-repeat |
data.<набор> | набор данных целиком, например для условия или связи |
frame.<ключ>, frame.content | параметр и содержимое вставки фрейма |
Если в имени поля опечатка, оно ничего не выводит и попадает в отчет сборки. Полный список полей с описанием каждого — в справочнике.
Другие способы записи
OnPress понимает и другую запись тех же возможностей, она работает так же. Атрибут data-op-bind с JSON: <h1 data-op-bind='{"text":"page.title"}'>Заголовок</h1>, в нем же условия if и unless, ключи html, md, picture и "empty":"clear", когда пустое значение должно очищать тег. Повтор комментариями: <!-- onpress/repeat {"source":"team"} -->…<!-- /onpress/repeat -->. Текст страницы и содержимое вставки служебными блоками <!-- onpress/content /--> и <!-- onpress/slot /-->. Значение там, где нет тега, например в <script>, — блоком <!-- onpress/value {"bind":"page.title"} /-->. Подробно все формы описывает справочник.
Вопросы и ответы
Что видит посетитель в HTML страницы?
На страницу попадает чистый HTML: значения на месте полей, без фигурных скобок, без атрибутов data-op-* и без <template> от повтора.
Почему поле ничего не подставило?
Проверьте пять причин. Скобки стоят внутри <code>, <script> или тега с data-op-raw. В скобках не имя поля, например {{ name }}. У поля нет значения или в имени опечатка — тогда оно попадает в список unresolved отчета сборки, который приходит в ответе записи фрейма. Поле item.* стоит вне data-op-repeat. В фильтре или условии ошибка, например опечатка в имени фильтра, — тогда OnPress показывает заглушку, а ошибку пишет в warnings ответа записи фрейма.
Как задать значение по умолчанию?
Напишите его заглушкой после ||: {{page.author_name || редакция}}. Чтобы убрать тег совсем, поставьте data-op-if с тем же полем.
Чем поле данных отличается от поля набора?
Поле набора — ключ в элементе данных, например title в наборе reviews. Поле данных — имя значения в шаблоне, например {{item.title}}: через него поле набора попадает на страницу.
Почему у фрейма пропала общая копия?
Во фрейм попало поле page.*. Список причин приходит в ответе записи фрейма полем live_reasons, подробно — сборка и копии.