Сайт вебмастера

Условные блоки в Alpine.js

02-12-2020Reading time ~ 6 min.Alpine.js 5590

Чтобы показать или скрыть блок используется директива x-show, однако существует ещё одна альтернатива — x-if. Работают они одинаково, с той разницей, что x-show можно применить к любому html-тэгу, а x-if работает только с тэгом TEMPLATE.

Этот тэг имеет особое значение в HTML 5 — по умолчанию он скрыт и применяется для «отложенного создания клиентского контента». Его содержимое является шаблоном/фрагментом, который инициализируется (отображается) через JavaScript. Тэг TEMPLATE является «базовым» при использовании веб-компонентов. В Alpine.js он играет туже самую роль, хотя и с небольшим ограничением — в него должен быть вложен тэг-контейнер (об этом ниже).

Таким образом вопрос использования x-if лежит скорее в плоскости предпочтений вебмастера.

Базовый пример

Сделаем аля-табы с использованием x-if.

<div x-data="{b: 0}">
    <button @click="b = 1" class="button">First</button>
    <button @click="b = 2" class="button">Second</button>
    <button @click="b = 0" class="button">Hide</button>
 
    <template x-if="b == 1">
        <div>First content</div>
    </template>
 
    <template x-if="b == 2">
        <div>Second content</div>
    </template>
</div>

Здесь алгоритм аналогичен предыдущему с x-show, только применяется к TEMPLATE.

Внутри этого тэга всегда должен быть корневой элемент. В данном случае это DIV. А вот такой вариант — ошибочный:

<template x-if="b == 2">
   Second content
</template>

Здесь нет корневого элемента и блок может работать неверно.

При использовании TEMPLATE мы можем использовать его html-разметку совершенно отдельно без привязки к Alpine. Это ещё одно отличие от x-show, где директивы прописываются в самом блоке. Здесь же можно вынести контент каждого блока в отдельные файлы и подключать их например через php-функцию require без опаски, что это сломает сам Alpine-код.

Формы

Использование x-if делает код немного понятней. Это можно использоваться например в формах, которые меняются в зависимости от выбора пользователя. Простой пример.

<div x-data="{b: false, s: 1}">
    <label><input type="checkbox" x-model="b"> Check me</label>
 
    <template x-if="!b">
         <span class="t-gray600 t-italic mar10-l animation-fade">Please, select this...</span>
    </template>
 
    <template x-if="b">
         <div class="mar15-tb animation-fade">
            <select x-model="s">
                <option value="1">First</option>
                <option value="2">Second</option>
            </select>
 
            <template x-if="s == 1">
                <span>First content</span>
            </template>
 
            <template x-if="s == 2">
                <span>Second content</span>
            </template>
        </div>
    </template>
</div>

Здесь два интерактивных элемента: чекбокс и выпадающий список. Когда чекбокс отмечен, появляется блок списка. В зависимости от его выбора, появляется и подсказка.

Двустороннее связывание данных в Alpine

Уж коли я привёл пример с полями ввода, то обратите внимание на директиву x-model. Она используется специально для полей ввода: input, textarea и select. В x-model указывается переменная, которая будет автоматом связана со значением этого поля (value).

<div x-data="{t: ''}">
    <div><input type="text" x-model="t"> <span x-text="t"></span></div>
    <div><button @click="t = 'Hi!'" class="button">Hi!</button></div>
</div>

Переменная t служит хранилищем значения input. Когда пользователь вводит какой-то текст, то он автоматически сохраняется в этой переменной. Но при этом, если мы ещё где-нибудь её изменим, то она автоматом изменит и значение input. То есть работает в двух направлениях, отсюда и название «двусторонняя передача данных» (two-way data binding).

Другие примеры

Вернёмся к x-if. Иногда при вёрстке необходимо поменять вывод в зависимости от каких-то условий, например для телефона нужно отобразить другой блок. Обычно мы это делаем с помощью @media-условий, но при желании можно сделать это и на Alpine.

Для затравки простой пример:

<div x-data="{small: false}" x-on:resize.window="small = window.outerWidth < 768">
    <template x-if="!small">
        <div>wide content</div>
    </template>
 
    <template x-if="small">
        <div>small content</div>
    </template>
</div>

Если поиграть шириной браузера, то будет выводиться либо один, либо другой контент. В данном примере мы «прицепились» к событию onresize и отслеживаем изменения ширины браузера.

С практической точки зрения, если нам потребуется больше вариантов (phone, tablet и т.д.), удобней было бы вынести всю «программную» часть в js-функцию и использовать её в x-spread.

Вначале код функции:

<script>
function screenInit() {
    return {
        small: false,
        phone: false,
        tablet: false,
        desktop: false,
 
        setWidth: function() {
            w = window.outerWidth;

            this.small = w <= 360;
            this.phone = (w > 360 && w <= 667);
            this.tablet = (w > 667 && w <= 768);
            this.desktop = w > 768;
        },
 
        trigger: {
            ['x-on:resize.window']() {
                this.setWidth();
            },
 
            ['x-on:load.window']() {
                this.setWidth();
            },
        },
    }
}
</script>

В ней мы задаём логические переменные, которые меняются в зависимости от ширины экрана и установленных пороговых значений. Событие x-on:load.window инициализирует их в момент загрузки страницы.

К сожалению директиву x-init в x-spread использовать нельзя.

Применять можно так:

<div x-data="screenInit()" x-spread="trigger">
    <template x-if="small">
        <div>small content</div>
    </template>
 
    <template x-if="phone">
        <div>phone content</div>
    </template>
 
    <template x-if="tablet">
        <div>tablet content</div>
    </template>
 
    <template x-if="desktop">
        <div>desktop content</div>
    </template>
 
    <template x-if="phone || tablet">
        <div>phone & tablet content</div>
    </template>
</div>

Вёрстка с таким кодом достаточно приятное занятие. :-)

Итого

С точки зрения вёрстки можно использовать и x-show, и x-if — поведение у них одинаково. Если html-код нужно оставить «чистым», то лучше его разместить с x-if.

Наверное ещё стоит отметить, что x-show работает с display: none; (чего хватает в большинстве случаев), а x-if — как настоящий веб-компоненты — через клонирование DOM-элемента. В каких-то сложных ситуациях, когда блок использует свойство display, которое не должно меняться, придётся использовать x-if.

Related Posts