788 lines
46 KiB
Markdown
788 lines
46 KiB
Markdown
# Alpine.js
|
||
|
||

|
||

|
||
[](https://alpinejs.codewithhugo.com/chat/)
|
||
|
||
Alpine.js предоставляет реактивность и декларативность как в больших фреймворках вроде Vue или React, но с меньшими затратами.
|
||
|
||
Вы сможете использовать обычный DOM, при этом изменяя поведение по своему усмотрению.
|
||
|
||
Можете думать о Alpine.js как о [Tailwind](https://tailwindcss.com/) для JavaScript.
|
||
|
||
> Замечание: синтаксис Alpine.js почти полностью заимствован из [Vue](https://ru.vuejs.org/) (а, соответственно, и из [Angular](https://angularjs.org/)). Я безмерно благодарен разработчикам этих инструментов за тот вклад, который они внесли в Web.
|
||
|
||
## Установка
|
||
|
||
**С помощью CDN:** Добавьте следующий `<script>` в конец секции `<head>`.
|
||
```html
|
||
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js" defer></script>
|
||
```
|
||
|
||
Вот и всё. Он инициализируется самостоятельно.
|
||
|
||
Для рабочего окружения рекомендуется использовать ссылку с конкретным номером версии, чтобы избежать неожиданных поломок после выпуска новых версий.
|
||
Например, чтобы использовать версию `2.8.2`:
|
||
```html
|
||
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.8.2/dist/alpine.min.js" defer></script>
|
||
```
|
||
|
||
**С помощью npm:** Установите пакет из npm.
|
||
```js
|
||
npm i alpinejs
|
||
```
|
||
|
||
Добавьте его в свой код.
|
||
```js
|
||
import 'alpinejs'
|
||
```
|
||
|
||
**Для поддержки IE11** используйте вместо указанных выше следующие скрипты:
|
||
```html
|
||
<script type="module" src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js"></script>
|
||
<script nomodule src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine-ie11.min.js" defer></script>
|
||
```
|
||
|
||
Паттерн, использующийся выше, называется [паттерн module/nomodule](https://philipwalton.com/articles/deploying-es2015-code-in-production-today/). Он позволяет автоматически загружать современный пакет в современных браузерах, а в IE11 и других устаревших браузерах – пакет для IE11.
|
||
|
||
## Использование
|
||
|
||
*Выпадающий список/Модальное окно*
|
||
```html
|
||
<div x-data="{ open: false }">
|
||
<button @click="open = true">Открыть</button>
|
||
|
||
<ul
|
||
x-show="open"
|
||
@click.away="open = false"
|
||
>
|
||
Содержимое
|
||
</ul>
|
||
</div>
|
||
```
|
||
|
||
*Вкладки*
|
||
```html
|
||
<div x-data="{ tab: 'foo' }">
|
||
<button :class="{ 'active': tab === 'foo' }" @click="tab = 'foo'">Foo</button>
|
||
<button :class="{ 'active': tab === 'bar' }" @click="tab = 'bar'">Bar</button>
|
||
|
||
<div x-show="tab === 'foo'">Вкладка Foo</div>
|
||
<div x-show="tab === 'bar'">Вкладка Bar</div>
|
||
</div>
|
||
```
|
||
|
||
Alpine.js можно использовать и для более серьезных выражений. Например, *предзагрузка HTML-содержимого при наведении мыши*.
|
||
|
||
```html
|
||
<div x-data="{ open: false }">
|
||
<button
|
||
@mouseenter.once="
|
||
fetch('/dropdown-partial.html')
|
||
.then(response => response.text())
|
||
.then(html => { $refs.dropdown.innerHTML = html })
|
||
"
|
||
@click="open = true"
|
||
>Показать</button>
|
||
|
||
<div x-ref="dropdown" x-show="open" @click.away="open = false">
|
||
Загрузка...
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
## Изучение
|
||
|
||
Всего в Alpine 14 директив:
|
||
|
||
| Директива | Описание |
|
||
| --- | --- |
|
||
| [`x-data`](#x-data) | Объявляет новый компонент и его данные. |
|
||
| [`x-init`](#x-init) | Выполняет переданное выражение, когда компонент инициализируется. |
|
||
| [`x-show`](#x-show) | Переключает `display: none;` на элементе, в зависимости от результата переданного выражения (true или false). |
|
||
| [`x-bind`](#x-bind) | Устанавливает значение атрибута равным результату переданного JS-выражения. |
|
||
| [`x-on`](#x-on) | Устанавливает обработчик события на элемент. Когда событие срабатывает, выполняет переданное JS-выражение. |
|
||
| [`x-model`](#x-model) | Добавляет "двустороннюю привязку данных" (two-way data binding) на элемент. Синхронизирует элемент и данные компонента. |
|
||
| [`x-text`](#x-text) | Устанавливает значение `innerText` элемента равным результату переданного JS-выражения. |
|
||
| [`x-html`](#x-html) | Устанавливает значение `innerHTML` элемента равным результату переданного JS-выражения. |
|
||
| [`x-ref`](#x-ref) | Удобный способ получения DOM-элементов вашего компонента. |
|
||
| [`x-if`](#x-if) | При невыполнении переданного условия полностью удаляет элемент из DOM. Должна использоваться в теге `<template>`. |
|
||
| [`x-for`](#x-for) | Создает новые DOM узлы для каждого элемента в массиве. Должна использоваться в теге `<template>`. |
|
||
| [`x-transition`](#x-transition) | Директивы для добавления css-классов различным стадиям перехода (transition) элемента. |
|
||
| [`x-spread`](#x-spread) | Позволяет привязывать объект с директивами Alpine к элементам, улучшая повторное использование кода. |
|
||
| [`x-cloak`](#x-cloak) | Атрибут удаляется после инициализации Alpine. Используется для скрытия элементов до DOM инициализации. |
|
||
|
||
И 6 магических свойств (magic properties):
|
||
|
||
| Магическое свойство | Описание |
|
||
| --- | --- |
|
||
| [`$el`](#el) | Получить DOM-узел корневого компонента. |
|
||
| [`$refs`](#refs) | Получить DOM-элементы компонента, отмеченные `x-ref`. |
|
||
| [`$event`](#event) | В обработчике события получить нативный объект браузера "Event". |
|
||
| [`$dispatch`](#dispatch) | Создать `CustomEvent` и вызвать его, используя `.dispatchEvent()`. |
|
||
| [`$nextTick`](#nexttick) | Выполнить переданное выражение ПОСЛЕ того, как Alpine сделает реактивное обновление DOM. |
|
||
| [`$watch`](#watch) | Выполнить переданный колбэк, когда наблюдаемое свойство компонента изменится. |
|
||
|
||
|
||
## Спонсоры
|
||
|
||
<img width="33%" src="https://refactoringui.nyc3.cdn.digitaloceanspaces.com/tailwind-logo.svg" alt="Tailwind CSS">
|
||
|
||
**Хочешь здесь своё лого? [Напиши мне сообщение в Twitter](https://twitter.com/calebporzio)**
|
||
|
||
## Сообщество проекта
|
||
|
||
* [Еженедельная рассылка AlpineJS](https://alpinejs.codewithhugo.com/newsletter/)
|
||
* [Spruce (глобальные данные между компонентами)](https://github.com/ryangjchandler/spruce)
|
||
* [Turbolinks Adapter](https://github.com/SimoTod/alpine-turbolinks-adapter)
|
||
* [Alpine Magic Helpers (полезные хелперы для Alpine)](https://github.com/KevinBatdorf/alpine-magic-helpers)
|
||
* [Awesome Alpine (ссылки на прочие проекты об Alpine)](https://github.com/ryangjchandler/awesome-alpine)
|
||
|
||
### Директивы
|
||
|
||
---
|
||
|
||
### `x-data`
|
||
|
||
**Пример:** `<div x-data="{ foo: 'bar' }">...</div>`
|
||
|
||
**Синтаксис:** `<div x-data="[JSON-объект]">...</div>`
|
||
|
||
`x-data` объявляет область видимости нового компонента с использованием переданного объекта данных.
|
||
|
||
Аналогична свойству `data` в компонентах Vue.
|
||
|
||
**Задание логики компонента отдельной функцией**
|
||
|
||
Можно получить данные (и задать поведение компонента) в повторно используемых функциях:
|
||
|
||
```html
|
||
<div x-data="dropdown()">
|
||
<button x-on:click="open">Открыть</button>
|
||
|
||
<div x-show="isOpen()" x-on:click.away="close">
|
||
// Dropdown
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
function dropdown() {
|
||
return {
|
||
show: false,
|
||
open() { this.show = true },
|
||
close() { this.show = false },
|
||
isOpen() { return this.show === true },
|
||
}
|
||
}
|
||
</script>
|
||
```
|
||
|
||
> **Для пользователей бандлеров (bundler)**. Alpine.js получает доступ к функциям только из глобальной области видимости (`window`). Вам необходимо явно присвоить свои функции объекту `window`, чтобы использовать их с `x-data`. Например, вот так: `window.dropdown = function () {}` (с Webpack, Rollup, Parcel и другими бандлерами функции, которые вы объявляете, по умолчанию принадлежать области видимости бандлера, а не `window`).
|
||
|
||
|
||
Вы также можете объединять несколько объектов с данными с помощью деструктуризации:
|
||
|
||
```html
|
||
<div x-data="{...dropdown(), ...tabs()}">
|
||
```
|
||
|
||
---
|
||
|
||
### `x-init`
|
||
**Пример:** `<div x-data="{ foo: 'bar' }" x-init="foo = 'baz'"></div>`
|
||
|
||
**Синтаксис:** `<div x-data="..." x-init="[выражение]"></div>`
|
||
|
||
`x-init` выполняет переданное выражение, когда компонент инициализируется.
|
||
|
||
Если вы хотите выполнить код ПОСЛЕ первоначальных обновлений DOM Alpine (наподобие хука `mounted()` во VueJS), то можете передать в `x-init` колбэк, и он выполнит его после инициализации:
|
||
|
||
`x-init="() => { // здесь уже есть доступ к состоянию после инициализации DOM // }"`
|
||
|
||
---
|
||
|
||
### `x-show`
|
||
**Пример:** `<div x-show="open"></div>`
|
||
|
||
**Синтаксис:** `<div x-show="[выражение]"></div>`
|
||
|
||
`x-show` переключает `display: none;` на элементе в зависимости от результата выполнения выражения (`true` или `false`).
|
||
|
||
**x-show.transition**
|
||
|
||
`x-show.transition` – удобный API для добавления к `x-show` CSS-переходов (transitions).
|
||
|
||
```html
|
||
<div x-show.transition="open">
|
||
Это содержимое будет иметь переходы при появлении и исчезновении.
|
||
</div>
|
||
```
|
||
|
||
| Директива | Описание |
|
||
| --- | --- |
|
||
| `x-show.transition` | Одновременный fade и scale. (opacity, scale: 0.95, timing-function: cubic-bezier(0.4, 0.0, 0.2, 1), duration-in: 150ms, duration-out: 75ms)
|
||
| `x-show.transition.in` | Переход только при появлении. |
|
||
| `x-show.transition.out` | Переход только при исчезновении. |
|
||
| `x-show.transition.opacity` | Использовать только fade. |
|
||
| `x-show.transition.scale` | Использовать только scale. |
|
||
| `x-show.transition.scale.75` | Кастомизация scale перехода `transform: scale(.75)`. |
|
||
| `x-show.transition.duration.200ms` | Устанавливает время перехода при появлении на 200мс. Переход при исчезновении будет равен половине этого значения (100мс). |
|
||
| `x-show.transition.origin.top.right` | Кастомизация места возникновения перехода `transform-origin: top right`. |
|
||
| `x-show.transition.in.duration.200ms.out.duration.50ms` | Различные длительности для переходов при появлении и исчезновении. |
|
||
|
||
> Замечание: Все эти модификаторы переходов могут использоваться в сочетании друг с другом. Это возможно (хоть и нелепо): `x-show.transition.in.duration.100ms.origin.top.right.opacity.scale.85.out.duration.200ms.origin.bottom.left.opacity.scale.95`
|
||
|
||
> Замечание: `x-show` будет ждать окончания переходов всех дочерних элементов. Можно изменить это поведение модификатором `.immediate`:
|
||
```html
|
||
<div x-show.immediate="open">
|
||
<div x-show.transition="open">
|
||
</div>
|
||
```
|
||
---
|
||
|
||
### `x-bind`
|
||
|
||
> Сокращенный синтаксис ":". Например: `:type="..."`
|
||
|
||
**Пример:** `<input x-bind:type="inputType">`
|
||
|
||
**Синтаксис:** `<input x-bind:[атрибут]="[выражение]">`
|
||
|
||
`x-bind` устанавливает значение атрибута равным результату JS-выражения. Выражение имеет доступ ко всем ключам хранилища данных компонента и будет обновляться каждый раз при обновлении данных.
|
||
|
||
> Замечание: обновление значения атрибута с `x-bind` будет происходить ТОЛЬКО при обновлении его зависимостей.
|
||
|
||
**`x-bind` для атрибутов class**
|
||
|
||
`x-bind` ведет себя немного иначе, когда привязан к атрибуту `class`.
|
||
|
||
Для css-классов необходимо передавать объект, где ключи – это имена классов, а значения – логические выражения, которые определяют применяются эти классы или нет.
|
||
|
||
Например:
|
||
`<div x-bind:class="{ 'hidden': foo }"></div>`
|
||
|
||
В этом примере, класс "hidden" будет применен только если значение выражения `foo` равно `true`.
|
||
|
||
**`x-bind` для логических атрибутов**
|
||
|
||
`x-bind` поддерживает логические атрибуты так же, как и атрибуты значения, используя переменную как условие или любое JS-выражение, которое разрешается в `true` или `false`.
|
||
|
||
Например:
|
||
```html
|
||
<!-- Дано: -->
|
||
<button x-bind:disabled="myVar">Нажми на меня</button>
|
||
|
||
<!-- Когда myVar == true: -->
|
||
<button disabled="disabled">Нажми на меня</button>
|
||
|
||
<!-- Когда myVar == false: -->
|
||
<button>Нажми на меня</button>
|
||
```
|
||
|
||
Это добавит или удалит атрибут `disabled`, в зависимости от того, равна `myVar` true или false.
|
||
|
||
Логические атрибуты поддерживаются в соответствии с [HTML спецификацией](https://html.spec.whatwg.org/multipage/indices.html#attributes-3:boolean-attribute), такие как, например, `disabled`, `readonly`, `required`, `checked`, `hidden`, `selected`, `open` и другие.
|
||
|
||
> Замечание: Если нужно вернуть логическое (true/false) значение атрибута в виде текста, например для `aria-*`, используйте метод `.toString()`. Например `:aria-expanded="isOpen.toString()"` вернёт строку `true` или `false`в зависимости от значения `isOpen`.
|
||
|
||
**Модификатор `.camel`**
|
||
**Пример:** `<svg x-bind:view-box.camel="myVar">`
|
||
|
||
Модификатор `camel` позволяет задать имя атрибута, которое будет преобразовано к синтаксису "camelCase". В данном примере значение будет присвоено атрибуту `viewBox`, а не `view-box`.
|
||
|
||
---
|
||
|
||
### `x-on`
|
||
|
||
> Сокращенный синтаксис "@": `@click="..."`
|
||
|
||
**Пример:** `<button x-on:click="foo = 'bar'"></button>`
|
||
|
||
**Синтаксис:** `<button x-on:[событие]="[выражение]"></button>`
|
||
|
||
`x-on` цепляет слушатель события на элемент, в котором был объявлен. Когда событие срабатывает, выполняется переданное JS-выражение. Можно использовать `x-on` с любым событием. См. полный список событий в [документации MDN](https://developer.mozilla.org/en-US/docs/Web/Events).
|
||
|
||
Если в этом выражении меняются какие-либо данные, другие элементы, "привязанные" к этим данным, также будут обновлены.
|
||
|
||
> Замечание: Также можно задать имя JS-функции.
|
||
|
||
**Пример:** `<button x-on:click="myFunction"></button>`
|
||
|
||
Это равноценно: `<button x-on:click="myFunction($event)"></button>`
|
||
|
||
**Модификатор `keydown`**
|
||
|
||
**Пример:** `<input type="text" x-on:keydown.escape="open = false">`
|
||
|
||
Можно обозначить конкретные клавиши для прослушивания, присоединяя их через точку к директиве `x-on:keydown`. Модификаторы – это значения `Event.key`, записанные в kebab-стиле.
|
||
|
||
Например: `enter`, `escape`, `arrow-up`, `arrow-down`
|
||
|
||
> Замечание: Можно также прослушивать комбинации с системными клавишами, например: `x-on:keydown.cmd.enter="foo"`
|
||
|
||
**Модификатор `.away`**
|
||
|
||
**Пример:** `<div x-on:click.away="showModal = false"></div>`
|
||
|
||
При добавлении модификатора `.away` обработчик события сработает, только когда событие произошло на другом источнике, а не на этом элементе или его потомках.
|
||
|
||
Это полезно для скрытия дропдаунов или модальных окон, когда пользователь кликает в другом месте экрана.
|
||
|
||
**Модификатор `.prevent`**
|
||
|
||
**Пример:** `<input type="checkbox" x-on:click.prevent>`
|
||
|
||
При добавлении `.prevent` обработчик вызовет `preventDefault` на сработавшем событии. В примере выше это приведет к тому, что чекбокс не будет отмечен при нажатии на него.
|
||
|
||
**Модификатор `.stop`**
|
||
|
||
**Пример:** `<div x-on:click="foo = 'bar'"><button x-on:click.stop></button></div>`
|
||
|
||
При добавлении `.stop` обработчик вызовет `stopPropagation` на сработавшем событии. В примере выше это приведет к тому, что событие "click" не всплывет от кнопки к внешнему `<div>`. Другими словами, когда пользователь нажимает на кнопку, `foo` не устанавливается в `'bar'`.
|
||
|
||
**Модификатор `.self`**
|
||
|
||
**Пример:** `<div x-on:click.self="foo = 'bar'"><button></button></div>`
|
||
|
||
При добавлении `.self` обработчик события сработает, только если `$event.target` – это сам элемент. В примере выше это приведет к тому, что событие "click", всплыв от кнопки к внешнему `<div>`, **не** вызовет его обработчик.
|
||
|
||
**Модификатор `.window`**
|
||
|
||
**Пример:** `<div x-on:resize.window="isOpen = window.outerWidth > 768 ? false : open"></div>`
|
||
|
||
При добавлении `.window` слушатель события установится не на узел DOM, на котором был вызван, а на глобальный объект window. Это используется, когда нужно изменить состояние компонента при изменении чего-либо в window, например, при событии "resize". В примере выше, когда ширина окна будет больше 768 пикселей, модальное окно закроется.
|
||
|
||
>Замечание: Также можно использовать модификатор `.document` для добавления слушателей к `document`.
|
||
|
||
**Модификатор `.once`**
|
||
|
||
**Пример:** `<button x-on:mouseenter.once="fetchSomething()"></button>`
|
||
|
||
При добавлении `.once` обработчик события будет вызван лишь единожды. Это полезно для вещей, которые нужно сделать только один раз, например, загрузка данных и т.п.
|
||
|
||
**Модификатор `.passive`**
|
||
|
||
**Пример:** `<button x-on:mousedown.passive="interactive = true"></button>`
|
||
|
||
Добавление модификатора `.passive` к слушателю события сделает его пассивным. Это значит, что `preventDefault()` не будет работать ни с какими обрабатываемыми событиями. Это может помочь например с производительностью при прокрутке на сенсорных устройствах.
|
||
|
||
**Модификатор `.debounce`**
|
||
|
||
**Пример:** `<input x-on:input.debounce="fetchSomething()">`
|
||
|
||
Модификатор `debounce` позволяет избавиться от ложных повторных вызовов обработчика события. Другими словами, обработчик НЕ будет вызван, пока не пройдет определенное количество времени с предыдущего вызова. Когда обработчик будет готов, будет вызван последний вызов.
|
||
|
||
Время ожидания по умолчанию 250 миллисекунд.
|
||
|
||
Вы также можете указать свое время:
|
||
|
||
```
|
||
<input x-on:input.debounce.750="fetchSomething()">
|
||
<input x-on:input.debounce.750ms="fetchSomething()">
|
||
```
|
||
|
||
**Модификатор `.camel`**
|
||
|
||
**Пример:** `<input x-on:event-name.camel="doSomething()">`
|
||
|
||
Модификатор `camel` позволяет задать событие, которое будет преобразовано к синтаксису "camelCase". В данном примере слушатель будет добавлен к событию `eventName`.
|
||
|
||
---
|
||
|
||
### `x-model`
|
||
**Пример:** `<input type="text" x-model="foo">`
|
||
|
||
**Синтаксис:** `<input type="text" x-model="[хранилище данных]">`
|
||
|
||
`x-model` добавляет элементу "двустороннюю привязку данных" (two-way data binding). Другими словами, значение поля ввода будет синхронизировано со значением в хранилище данных компонента.
|
||
|
||
> Замечание: `x-model` достаточно умен, чтобы замечать изменения в текстовых полях ввода, чекбоксах, радио-кнопках, textarea, select, и множественных select. В данных сценариях `x-model` ведет себя аналогично `v-model` [во Vue](https://ru.vuejs.org/v2/guide/forms.html).
|
||
|
||
**Модификатор `.number`**
|
||
**Пример:** `<input x-model.number="age">`
|
||
|
||
Модификатор `number` преобразует входное значение в число. Если оно не может быть преобразовано, то возвращается исходное значение.
|
||
|
||
**Модификатор `.debounce`**
|
||
**Пример:** `<input x-model.debounce="search">`
|
||
|
||
Модификатор `debounce` позволяет избавиться от ложных повторных изменений значения. Другими словами, обработчик НЕ будет вызван, пока не пройдет определенное количество времени с предыдущего вызова. Когда обработчик будет готов, будет вызван последний вызов.
|
||
|
||
Время ожидания по умолчанию 250 миллисекунд.
|
||
|
||
Вы также можете указать свое время:
|
||
|
||
```
|
||
<input x-model.debounce.750="search">
|
||
<input x-model.debounce.750ms="search">
|
||
```
|
||
|
||
---
|
||
|
||
### `x-text`
|
||
**Пример:** `<span x-text="foo"></span>`
|
||
|
||
**Синтаксис:** `<span x-text="[выражение]"`
|
||
|
||
`x-text` устанавливает значение `innerText` элемента равным результату переданного JS-выражения. Другими словами, работает аналогично `x-bind`, но не для атрибута, а для `innerText` элемента.
|
||
|
||
---
|
||
|
||
### `x-html`
|
||
**Пример:** `<span x-html="foo"></span>`
|
||
|
||
**Синтаксис:** `<span x-html="[выражение]"`
|
||
|
||
`x-html` устанавливает значение `innerHTML` элемента равным результату переданного JS-выражения. Другими словами, работает аналогично `x-bind`, но не для атрибута, а для `innerHTML` элемента.
|
||
|
||
> :warning: **Используйте только надежные источники контента и никогда не используйте контент, предоставленный пользователем.** :warning:
|
||
>
|
||
> Динамически отрендеренный HTML от третьих сторон может легко привести к [XSS](https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting) уязвимостям.
|
||
|
||
---
|
||
|
||
### `x-ref`
|
||
**Пример:** `<div x-ref="foo"></div><button x-on:click="$refs.foo.innerText = 'bar'"></button>`
|
||
|
||
**Синтаксис:** `<div x-ref="[имя рефа]"></div><button x-on:click="$refs.[имя рефа].innerText = 'bar'"></button>`
|
||
|
||
`x-ref` предоставляет удобный способ получения DOM-элементов ваших компонентов. При установлении атрибута `x-ref` на элемент, вы делаете его доступным всем обработчикам событий в объекте `$refs`.
|
||
|
||
Это удобная альтернатива установке id и использования `document.querySelector`.
|
||
|
||
> Замечание: при необходимости также можно привязывать x-ref динамические значения: `<span :x-ref="item.id"></span>`.
|
||
|
||
---
|
||
|
||
### `x-if`
|
||
**Пример:** `<template x-if="true"><div>Какой-то элемент</div></template>`
|
||
|
||
**Синтаксис:** `<template x-if="[выражение]"><div>Какой-то элемент</div></template>`
|
||
|
||
В случаях, когда `x-show` недостаточно (`x-show` устанавливает элементу `display: none`, если выражение ложно), можно использовать `x-if`, чтобы полностью удалить элемент из DOM.
|
||
|
||
Alpine не использует Virtual DOM, поэтому важно, чтобы `x-if` использовался в теге `<template></template>`.
|
||
|
||
> Замечание: Внутри тега `<template></template>` с `x-if` должен быть лишь один корневой элемент.
|
||
|
||
> Замечание: При использовании `template` в тэге `svg`, используйте [полифил](https://github.com/alpinejs/alpine/issues/637#issuecomment-654856538). Он должен быть запущен до инициализации Alpine.js.
|
||
|
||
---
|
||
|
||
### `x-for`
|
||
**Пример:**
|
||
```html
|
||
<template x-for="item in items" :key="item">
|
||
<div x-text="item"></div>
|
||
</template>
|
||
```
|
||
|
||
> Замечание: привязка `:key` опциональна, хотя КРАЙНЕ рекомендуется.
|
||
|
||
`x-for` используется для создания новых DOM-узлов для каждого элемента в массиве. `x-for` похоже на `v-for` во Vue, с одним отличием: `x-for` может использовался только в теге `<template></template>`.
|
||
|
||
Если вы хотите получить доступ к индексу текущей итерации, используйте следующий синтаксис:
|
||
|
||
```html
|
||
<template x-for="(item, index) in items" :key="index">
|
||
<!-- Если необходимо, вы также можете ссылаться на "index" внутри итерации. -->
|
||
<div x-text="index"></div>
|
||
</template>
|
||
```
|
||
Если нужно получить доступ к массиву данных (collection) внутри цикла, используйте такой синтаксис:
|
||
|
||
```html
|
||
<template x-for="(item, index, collection) in items" :key="index">
|
||
<!-- Можно ссылаться на массив "collection" -->
|
||
<!-- Текущий элемент -->
|
||
<div x-text="item"></div>
|
||
<!-- или так -->
|
||
<div x-text="collection[index]"></div>
|
||
<!-- Предыдущий элемент -->
|
||
<div x-text="collection[index - 1]"></div>
|
||
</template>
|
||
```
|
||
|
||
> Замечание: Внутри тега `<template></template>` с `x-for` должен быть лишь один корневой элемент.
|
||
|
||
> Замечание: При использовании `template` в тэге `svg`, используйте [полифил](https://github.com/alpinejs/alpine/issues/637#issuecomment-654856538). Он должен быть запущен до инициализации Alpine.js.
|
||
|
||
#### Вложенные `x-for`
|
||
Можно вкладывать `x-for` друг в друга, но НУЖНО оборачивать каждый цикл в какой-нибудь элемент. Например:
|
||
|
||
```html
|
||
<template x-for="item in items">
|
||
<div>
|
||
<template x-for="subItem in item.subItems">
|
||
<div x-text="subItem"></div>
|
||
</template>
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
#### Количество итераций
|
||
|
||
Alpine поддерживает синтаксис `i in n`, где `n` число, указывающее на количество итераций цикла.
|
||
|
||
```html
|
||
<template x-for="i in 10">
|
||
<span x-text="i"></span>
|
||
</template>
|
||
```
|
||
|
||
---
|
||
|
||
### `x-transition`
|
||
**Пример:**
|
||
```html
|
||
<div
|
||
x-show="open"
|
||
x-transition:enter="transition ease-out duration-300"
|
||
x-transition:enter-start="opacity-0 transform scale-90"
|
||
x-transition:enter-end="opacity-100 transform scale-100"
|
||
x-transition:leave="transition ease-in duration-300"
|
||
x-transition:leave-start="opacity-100 transform scale-100"
|
||
x-transition:leave-end="opacity-0 transform scale-90"
|
||
>...</div>
|
||
```
|
||
|
||
```html
|
||
<template x-if="open">
|
||
<div
|
||
x-transition:enter="transition ease-out duration-300"
|
||
x-transition:enter-start="opacity-0 transform scale-90"
|
||
x-transition:enter-end="opacity-100 transform scale-100"
|
||
x-transition:leave="transition ease-in duration-300"
|
||
x-transition:leave-start="opacity-100 transform scale-100"
|
||
x-transition:leave-end="opacity-0 transform scale-90"
|
||
>...</div>
|
||
</template>
|
||
```
|
||
|
||
> Пример выше использует классы из [Tailwind CSS](https://tailwindcss.com)
|
||
|
||
Alpine предлагает 6 разных transition-директив для добавления классов к различным стадиям перехода элемента от состояния "скрытого" к "видимому". Все эти директивы работают как с `x-show`, так и с `x-if`.
|
||
|
||
Они ведут себя абсолютно так же, как transition-директивы во VueJS, но у них более понятные названия:
|
||
|
||
| Директива | Описание |
|
||
| --- | --- |
|
||
| `:enter` | Применяется в ходе всей фазы появления. |
|
||
| `:enter-start` | Добавляется до введения элемента, удаляется на следующий фрейм после с введения элемента. |
|
||
| `:enter-end` | Добавляется на следующий фрейм после с введения элемента (одновременно с удалением `enter-start`), удаляется, когда переход/анимация заканчивается.
|
||
| `:leave` | Применяется в ходе всей фазы исчезновения. |
|
||
| `:leave-start` | Добавляется, как только вызвано исчезновение, удаляется на следующий фрейм. |
|
||
| `:leave-end` | Добавляется на следующий фрейм, как только вызвано исчезновение (одновременно с удалением `leave-start`), удаляется, когда переход/анимация заканчивается.
|
||
|
||
---
|
||
|
||
### `x-spread`
|
||
**Пример:**
|
||
```html
|
||
<div x-data="dropdown()">
|
||
<button x-spread="trigger">Открыть</button>
|
||
|
||
<span x-spread="dialogue">Содержимое</span>
|
||
</div>
|
||
|
||
<script>
|
||
function dropdown() {
|
||
return {
|
||
open: false,
|
||
trigger: {
|
||
['@click']() {
|
||
this.open = true
|
||
},
|
||
},
|
||
dialogue: {
|
||
['x-show']() {
|
||
return this.open
|
||
},
|
||
['@click.away']() {
|
||
this.open = false
|
||
},
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
```
|
||
|
||
`x-spread` позволяет вынести привязки Alpine из элементов в объект (он может повторно использоваться в других компонентах).
|
||
|
||
Ключи объекта – это директивы (любые, в том числе и с модификаторами), а значения – колбэки, с которыми будет работать Alpine.
|
||
|
||
> Замечание: Единственная особенность при работе с x-spread – это то, как обрабатывается `x-for`. Когда директива, используемая в x-spread – это `x-for`, в колбэке необходимо возвращать выражение в виде строки. К примеру: `['x-for']() { return 'item in items' }`.
|
||
|
||
> Замечание: Директивы `x-data` и `x-init` не могут использоваться в "spread"-объекте.
|
||
|
||
---
|
||
|
||
### `x-cloak`
|
||
**Пример:** `<div x-data="{}" x-cloak></div>`
|
||
|
||
Атрибуты `x-cloak` удаляются с элементов, когда Alpine будет проинициализирован. Это полезно для скрытия элемента до построения DOM. Для использования `x-cloak` необходимо добавить следующие css-правила:
|
||
|
||
```html
|
||
<style>
|
||
[x-cloak] { display: none; }
|
||
</style>
|
||
```
|
||
|
||
### Магические свойства
|
||
|
||
> Не считая `$el`, магические свойства **не доступны внутри `x-data`**, так как компонент еще не инициализирован.
|
||
|
||
---
|
||
|
||
### `$el`
|
||
**Пример:**
|
||
```html
|
||
<div x-data>
|
||
<button @click="$el.innerHTML = 'foo'">Замени меня на "foo"</button>
|
||
</div>
|
||
```
|
||
|
||
`$el` – магическое свойство, которое используется для получения корневого DOM-узла компонента.
|
||
|
||
### `$refs`
|
||
**Пример:**
|
||
```html
|
||
<span x-ref="foo"></span>
|
||
|
||
<button x-on:click="$refs.foo.innerText = 'bar'"></button>
|
||
```
|
||
|
||
`$refs` – это магическое свойство, которое используется для получения DOM-элементов внутри компонента, помеченных `x-ref`. Оно используется, когда нужно вручную манипулировать элементами DOM.
|
||
|
||
---
|
||
|
||
### `$event`
|
||
**Пример:**
|
||
```html
|
||
<input x-on:input="alert($event.target.value)">
|
||
```
|
||
|
||
`$event` – это магическое свойство, которое можно использовать в слушателе событий для получения нативного объекта "Event".
|
||
|
||
> Замечание: свойство $event доступно только в DOM-выражениях.
|
||
|
||
Если нужно получить доступ к $event внутри функции JavaScript, вы можете передать его как параметр:
|
||
|
||
`<button x-on:click="myFunction($event)"></button>`
|
||
|
||
---
|
||
|
||
### `$dispatch`
|
||
**Пример:**
|
||
```html
|
||
<div @custom-event="console.log($event.detail.foo)">
|
||
<button @click="$dispatch('custom-event', { foo: 'bar' })"></button>
|
||
<!-- После нажатия кнопки выводит в консоль "bar" -->
|
||
</div>
|
||
```
|
||
|
||
**Примечание по распространению событий (event propagation)**
|
||
|
||
Когда требуется перехватить событие, вызванное из узла на том же уровне вложенности, можно использовать модификатор [`.window`](https://github.com/alpinejs/alpine/blob/master/README.ru.md#x-on):
|
||
|
||
**Пример неверного использования:**
|
||
|
||
```html
|
||
<div x-data>
|
||
<span @custom-event="console.log($event.detail.foo)"></span>
|
||
<button @click="$dispatch('custom-event', { foo: 'bar' })"></button>
|
||
<div>
|
||
```
|
||
|
||
> Это не будет работать, потому что, когда вызывается `custom-event`, он сразу всплывает ([event bubbling](https://en.wikipedia.org/wiki/Event_bubbling)) к родителю `div`.
|
||
|
||
**Диспетчеризация для компонентов**
|
||
|
||
Вы также можете использовать предыдущую технику для общения компонентов друг с другом:
|
||
|
||
**Пример:**
|
||
|
||
```html
|
||
<div x-data @custom-event.window="console.log($event.detail)"></div>
|
||
|
||
<button x-data @click="$dispatch('custom-event', 'Hello World!')"></button>
|
||
<!-- При нажатии в консоль выведется "Hello World!". -->
|
||
```
|
||
|
||
`$dispatch` – это удобное сокращение для создания `CustomEvent` (пользовательские события) и их вызова с помощью `.dispatchEvent()`. Существует множество сценариев использования передачи данных между компонентами с помощью пользовательских событий. [Пройдите по этой ссылке](https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events), чтобы больше узнать о системе, лежащей в основе `CustomEvent` в браузерах.
|
||
|
||
Любые данные, переданные как второй параметр в `$dispatch('some-event', { some: 'data' })`, становятся доступны через свойство "detail" события: `$event.detail.some`. Добавление событию пользовательских данных через свойство `.detail` – стандартная практика для `CustomEvent` в браузерах. [Подробнее здесь](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail).
|
||
|
||
Вы также можете использовать `$dispatch()` для вызова обновления данных в привязках `x-model`. Например:
|
||
|
||
```html
|
||
<div x-data="{ foo: 'bar' }">
|
||
<span x-model="foo">
|
||
<button @click="$dispatch('input', 'baz')"></button>
|
||
<!-- После нажатия кнопки, `x-model` перехватит всплывающее событие "input" (oninput), и обновит foo на "baz". -->
|
||
</span>
|
||
</div>
|
||
```
|
||
|
||
> Замечание: Свойство $dispatch доступно только в DOM-выражениях.
|
||
|
||
Если нужен доступ к $dispatch внутри JavaScript-функции, вы можете передать его напрямую:
|
||
|
||
`<button x-on:click="myFunction($dispatch)"></button>`
|
||
|
||
---
|
||
|
||
### `$nextTick`
|
||
**Пример:**
|
||
```html
|
||
<div x-data="{ fruit: 'яблоко' }">
|
||
<button
|
||
x-on:click="
|
||
fruit = 'груша';
|
||
$nextTick(() => { console.log($event.target.innerText) });
|
||
"
|
||
x-text="fruit"
|
||
></button>
|
||
</div>
|
||
```
|
||
|
||
`$nextTick` – это магическое свойство, которое выполняет переданное выражение только ПОСЛЕ того, как Alpine реактивно обновит DOM. Это полезно в тех случаях, когда вы хотите взаимодействовать с состоянием DOM, ПОСЛЕ того, как оно отразит сделанное вами обновление данных.
|
||
|
||
---
|
||
|
||
### `$watch`
|
||
**Пример:**
|
||
```html
|
||
<div x-data="{ open: false }" x-init="$watch('open', value => console.log(value))">
|
||
<button @click="open = ! open">Toggle Open</button>
|
||
</div>
|
||
```
|
||
|
||
Магический метод `$watch` позволяет следить за выбранным свойством компонента. В примере выше при нажатии на кнопку: 1) значение `open` изменится; 2) выполнится переданный в `$watch` колбэк; 3) в консоль выведется новое значение.
|
||
|
||
## Безопасность
|
||
Если вы нашли уязвимость, пожалуйста, отправьте письмо на [calebporzio@gmail.com]().
|
||
|
||
Alpine полагается на собственную реализацию, которая использует объект `Function` для оценки своих директив. Несмотря на то, что он безопаснее, чем `eval()`, его использование запрещено в некоторых средах, таких как Google Chrome App, т.е. использующих Политику защиты контента (CSP).
|
||
|
||
Если вы используете Alpine на веб-сайте, имеющем дело с конфиденциальными данными и требующим [CSP](https://csp.withgoogle.com/docs/strict-csp.html), вы должны включить `unsafe-eval` в свою политику. Правильно настроенная политика поможет защитить ваших пользователей при использовании их личных или финансовых данных.
|
||
|
||
Поскольку политика применяется ко всем скриптам на вашей странице, важно, чтобы другие внешние библиотеки, которые используются на сайте, были тщательно проверены, чтобы убедиться, что они заслуживают доверия, и не будут создавать XSS-уязвимость с помощью функции `eval()` или манипулировать DOM для внедрения вредоносного кода на вашу страницу.
|
||
|
||
## Планы на третью версию
|
||
* Перейти с `x-ref` на `ref` для соответствия с Vue?
|
||
* Добавить `Alpine.directive()`
|
||
* Добавить `Alpine.component('foo', {...})` (с магическим методом `__init()`)
|
||
* Вызывать Alpine-события для "loaded", "transition-start", и т.д. ([#299](https://github.com/alpinejs/alpine/pull/299)) ?
|
||
* Удалить синтаксис объекта (и массива) у `x-bind:class="{ 'foo': true }"` ([#236](https://github.com/alpinejs/alpine/pull/236), чтобы добавить поддержку синтаксиса объекта для атрибута `style`)
|
||
* Улучшить изменение реактивности `x-for` ([#165](https://github.com/alpinejs/alpine/pull/165))
|
||
* Добавить поддержку "deep watching" ([#294](https://github.com/alpinejs/alpine/pull/294))
|
||
* Добавить сокращение для `$el`
|
||
* Изменить `@click.away` на `@click.outside`?
|
||
|
||
## Лицензия
|
||
|
||
Copyright © 2019-2021 Caleb Porzio и другие
|
||
|
||
Лицензировано по лицензии MIT, смотрите [LICENSE.md](LICENSE.md) для подробностей.
|