Перейти к содержимому

Liquid: основы

Шаблон блока — это Liquid. Вы пишете разметку, а вместо конкретных заголовков, ссылок и картинок подставляете переменные: значения приходят из полей блока, заполненных на странице.

<section class="hero">
<h1>{{ props.title }}</h1>
{% if props.subtitle != '' %}<p>{{ props.subtitle }}</p>{% endif %}
</section>

Один шаблон — сколько угодно наборов значений: тот же блок на десяти страницах выглядит по-разному, а разметка правится в одном месте.

Часть блока Liquid
Шаблон (template) да
CSS блока нет — отдаётся как есть
JS блока нет — отдаётся как есть
Шаблон макета да, но контекст другой — см. Что доступно в контексте

Переменную в CSS не подставить. Нужен параметр из поля — выводите его инлайн-стилем в шаблоне или классом-модификатором:

<div class="hero hero--{{ props.theme | default: 'light' }}"
style="max-width: {{ props.max_width | default: '800px' }}">

Вывод — двойные фигурные скобки. Результат подставляется в HTML как есть, автоматического экранирования нет:

{{ props.title }}
{{ props.title | escape }} {# если в поле может оказаться разметка #}

Теги — управляющие конструкции в {% %}:

{% if props.show_button %}
<a href="{{ props.url }}">{{ props.text }}</a>
{% endif %}

Фильтры — преобразование значения после |, их можно выстраивать цепочкой:

{{ props.title | strip | truncate: 60 | upcase }}
{% if props.title != '' %}{% elsif props.subtitle != '' %}{% else %}{% endif %}
{% unless props.hide_header %}{% endunless %}
{% case props.size %}
{% when 'sm' %}<div class="card card--sm">
{% when 'lg' %}<div class="card card--lg">
{% else %}<div class="card">
{% endcase %}

Операторы: ==, !=, >, <, >=, <=, and, or, contains.

{% for slide in props.slides %}
<div class="slide slide--{{ forloop.index }}">
<h3>{{ slide.title }}</h3>
{% if forloop.first %}<span class="badge">Новинка</span>{% endif %}
</div>
{% else %}
<p>Слайды не добавлены</p>
{% endfor %}

Внутри цикла элемент адресуется без префикса props — переменной цикла (slide.title). Доступны forloop.index, forloop.index0, forloop.first, forloop.last, forloop.length; работают {% break %} и {% continue %}.

Индекс и диапазон тоже поддерживаются: {{ props.slides[0].title }}, {% for i in (1..3) %}.

{% assign css_class = 'hero hero--' | append: props.theme %}
{% capture button_label %}{{ props.text }}{% endcapture %}
<div class="{{ css_class }}"><a>{{ button_label }}</a></div>

Эти 55 фильтров работают одинаково и в предпросмотре, и при публикации.

Группа Фильтры
Строки append prepend capitalize upcase downcase strip lstrip rstrip strip_html strip_newlines newline_to_br replace replace_first replace_last remove remove_first remove_last slice split truncate truncatewords escape escape_once url_encode url_decode
Числа plus minus times divided_by modulo abs ceil floor round at_least at_most
Массивы size first last join sort sort_natural reverse uniq compact concat map sum where reject find find_index has
Прочее default date

Пример работы date с полем типа date:

{{ props.published_at | date: '%d.%m.%Y' }}
{{ 'now' | date: '%Y' }}

Дефис внутри скобок съедает пробелы и переносы с этой стороны — разметка остаётся читаемой, а вывод чистым:

{%- for tag in props.tags -%}
<span>{{- tag.name -}}</span>
{%- endfor -%}
{% comment %} Пояснение, в HTML не попадёт {% endcomment %}
{% raw %}{{ этот_текст_не_рендерится }}{% endraw %}

{% include %}, {% render %}, {% layout %} и любые другие подключения файлов не работают: шаблон блока самодостаточен, файловой системы шаблонов у движка нет. Общий кусок разметки не выносится в партиал — он либо копируется, либо становится отдельным блоком.

Ошибку синтаксиса или рендеринга блок не роняет наружу: вместо разметки блока на страницу попадает HTML-комментарий, а публикация продолжается.

<!-- Liquid render error: Expected Identifier, got | -->

У макета поведение обратное: ошибка в шаблоне макета останавливает публикацию с сообщением, потому что страница без макета бессмысленна.


Источники: backend/src/Publishing/BlockRenderer.php, backend/src/Publishing/LayoutRenderer.php, front/src/lib/liquid.ts, keepsuit/liquid (src/Filters/StandardFilters.php, src/Extensions/StandardExtension.php), liquidjs@10.