Основы
Alpine.js интегрирован в MoonShine "из коробки" и предоставляет декларативный подход к созданию динамического поведения прямо в HTML-разметке. Это позволяет легко добавлять такие функции как:
- Динамическое скрытие/отображение элементов,
- Обработка событий,
- Реактивность,
- Асинхронные запросы,
- Анимации и переходы.
Благодаря своей легковесности и простоте синтаксиса, Alpine.js отлично подходит для задач админ-панели, не утяжеляя при этом ваше приложение.
Несмотря на то, что Alpine.js является рекомендуемым решением для MoonShine, вы не ограничены в выборе JavaScript-инструментов или использования ванильного JS.
Рекомендуем ознакомится с Alpine.js.
Создание компонента
Давайте попробуем создать собственный компонент.
php artisan moonshine:component MyComponentphp artisan moonshine:component MyComponent
Путь оставим как предлагает MoonShine - /resources/views/admin/components/my-component.blade.php.
Внутри добавим x-data с наименованием нашего компонента, тем самым мы укажем, что область внутри является компонентом Alpine.js.
<div x-data="myComponent"></div><div x-data="myComponent"></div>
Далее создадим скрипт, где в дальнейшем реализуем логику компонента Alpine.js.
document.addEventListener("alpine:init", () => {Alpine.data("myComponent", () => ({init() {// ...},}))})document.addEventListener("alpine:init", () => {Alpine.data("myComponent", () => ({init() {// ...},}))})
Для наглядности мы показали вам скрипт прямо в Blade, но рекомендуем выносить компоненты в отдельные JS файлы и подключать их через AssetManager.
Alpine.js уже установлен и запущен (window.Alpine), повторная инициализация приведет к ошибке.
События
Благодаря JS событиям вы можете удобно взаимодействовать с MoonShine! Обновлять формы, таблицы, области, вызывать модальные окна, сбрасывать формы и многое другое. Вы также можете создавать собственные события в JS.
Стандартные события
В админ-панели MoonShine определено несколько стандартных событий, названия которых удобно получать через enum JsEvent, но вы также можете их вызвать из JS.
fragment_updated:{componentName}(JsEvent::FRAGMENT_UPDATED) - обновление фрагмента,table_updated:{componentName}(JsEvent::TABLE_UPDATED) - обновление таблицы,table_reindex:{componentName}(JsEvent::TABLE_REINDEX) - обновление индекса таблицы при сортировке,table_row_updated:{componentName}-{row-id}(JsEvent::TABLE_ROW_UPDATED) - обновление строки таблицы,cards_updated:{componentName}(JsEvent::CARDS_UPDATED) - обновление списка карточек,form_reset:{componentName}(JsEvent::FORM_RESET) - сброс формы,form_submit:{componentName}(JsEvent::FORM_SUBMIT) - отправка формы,modal_toggled:{componentName}(JsEvent::MODAL_TOGGLED) - открытие / закрытие модального окна,off_canvas_toggled:{componentName}(JsEvent::OFF_CANVAS_TOGGLED) - открытие / закрытиеOffCanvas,popover_toggled:{componentName}(JsEvent::POPOVER_TOGGLED) - открытие / закрытиеOffCanvas,toast:{componentName}(JsEvent::TOAST) - вызов Toast,show_when_refresh:{componentName}(JsEvent::SHOW_WHEN_REFRESH) - обновить состоянияshowWhen,
Вызов событий
Давайте вспомним как это делается на стороне backend:
Modal::make('Title', 'Content')->name('my-modal'),ActionButton::make('Show modal window', '/endpoint')->async(events: [AlpineJs::event(JsEvent::MODAL_TOGGLED, 'my-modal')])Modal::make('Title', 'Content')->name('my-modal'),ActionButton::make('Show modal window', '/endpoint')->async(events: [AlpineJs::event(JsEvent::MODAL_TOGGLED, 'my-modal')])
Но раздел посвящен frontend и события могут быть вызваны с использованием нативных методов JS:
document.addEventListener("DOMContentLoaded", () => {this.dispatchEvent(new CustomEvent("modal_toggled:my-modal"))})document.addEventListener("DOMContentLoaded", () => {this.dispatchEvent(new CustomEvent("modal_toggled:my-modal"))})
Или с использованием магического метода $dispatch() из Alpine.js:
this.$dispatch('modal_toggled:my-modal')this.$dispatch('modal_toggled:my-modal')
Вызов событий через Response
В MoonShine можно возвращать события в MoonShineJsonResponse, которые затем будут вызваны.
Для этого нужно использовать метод events().
events(array $events)events(array $events)
$events- массив событий, которые нужно вызвать.
use MoonShine\Laravel\Http\Responses\MoonShineJsonResponse;use MoonShine\Support\AlpineJs;use MoonShine\Support\Enums\JsEvent;// ...return MoonShineJsonResponse::make()->events([AlpineJs::event(JsEvent::TABLE_UPDATED, 'index'),]);
use MoonShine\Laravel\Http\Responses\MoonShineJsonResponse;use MoonShine\Support\AlpineJs;use MoonShine\Support\Enums\JsEvent;// ...return MoonShineJsonResponse::make()->events([AlpineJs::event(JsEvent::TABLE_UPDATED, 'index'),]);
Blade директива
Blade директивы используются для быстрого объявления событий для компонентов.
@defineEvent
Директива для удобного объявления события в HTML.
@defineEvent(string|JsEvent $event,?string $name = null,?string $call = null,array $params = [],)@defineEvent(string|JsEvent $event,?string $name = null,?string $call = null,array $params = [],)
$event- событие,$name- имя компонента,$call- функция обратного вызова,$params- параметры события.
<div x-data="myComponent"{{-- @table-updated-index.window="asyncRequest" --}}@defineEvent('table-updated', 'index', 'asyncRequest')></div><div x-data="myComponent"{{-- @table-updated-index.window="asyncRequest" --}}@defineEvent('table-updated', 'index', 'asyncRequest')></div>
@defineEventWhen
С условием, будет ли добавлено или нет.
@defineEventWhen(mixed $condition,string|JsEvent $event,?string $name = null,?string $call = null,array $params = [],)@defineEventWhen(mixed $condition,string|JsEvent $event,?string $name = null,?string $call = null,array $params = [],)
$condition- условие,$event- событие,$name- имя компонента,$call- функция обратного вызова,$params- параметры события.
<div x-data="myComponent"{{-- @table-updated-index.window="asyncRequest" --}}@defineEventWhen(true, 'table-updated', 'index', 'asyncRequest')></div><div x-data="myComponent"{{-- @table-updated-index.window="asyncRequest" --}}@defineEventWhen(true, 'table-updated', 'index', 'asyncRequest')></div>
Вспомогательный класс AlpineJs для формирования событий.
AlpineJs::event()
AlpineJs::event(string|JsEvent $event,?string $name = null,array $params = [],)AlpineJs::event(string|JsEvent $event,?string $name = null,array $params = [],)
$event- событие,$name- имя компонента,$params- параметры события.
use MoonShine\UI\Components\FormBuilder;use MoonShine\Support\Enums\JsEvent;use MoonShine\Support\AlpineJs;FormBuilder::make('/crud/update')->name('main-form')->async(events: [AlpineJs::event(JsEvent::TABLE_UPDATED, 'index', ['var' => 'foo'])])use MoonShine\UI\Components\FormBuilder;use MoonShine\Support\Enums\JsEvent;use MoonShine\Support\AlpineJs;FormBuilder::make('/crud/update')->name('main-form')->async(events: [AlpineJs::event(JsEvent::TABLE_UPDATED, 'index', ['var' => 'foo'])])
AlpineJs::eventBlade()
AlpineJs::eventBlade(string|JsEvent $event,?string $name = null,?string $call = null,array $params = [])AlpineJs::eventBlade(string|JsEvent $event,?string $name = null,?string $call = null,array $params = [])
$event- событие,$name- имя компонента,$call- функция обратного вызова,$params- параметры события.
FormBuilder::make('/crud/update')->name('main-form')->customAttributes([// @form-reset-main-form.window="formReset"AlpineJs::eventBlade(JsEvent::FORM_RESET, 'main-form') => 'formReset',])FormBuilder::make('/crud/update')->name('main-form')->customAttributes([// @form-reset-main-form.window="formReset"AlpineJs::eventBlade(JsEvent::FORM_RESET, 'main-form') => 'formReset',])
Глобальный класс
Мы предоставляем глобальный класс MoonShine для удобного взаимодействия в клиентской части.
Request
MoonShine.request(ctx, '/url', method = 'get', body = {}, headers = {}, data = {})MoonShine.request(ctx, '/url', method = 'get', body = {}, headers = {}, data = {})
Toast
MoonShine.ui.toast('Hello world', 'success')MoonShine.ui.toast('Hello world', 'success')
Modal
Открыть/Закрыть Modal
MoonShine.ui.toggleModal('modal-name')MoonShine.ui.toggleModal('modal-name')
OffCanvas
Открыть/Закрыть OffCanvas
MoonShine.ui.toggleOffCanvas('canvas-name')MoonShine.ui.toggleOffCanvas('canvas-name')
Iterable
Reorder
MoonShine.iterable.sortable(container,url,group,events,attributes = {handle: '.handle'},function(evt) {// ...})MoonShine.iterable.sortable(container,url,group,events,attributes = {handle: '.handle'},function(evt) {// ...})
Переиндексация имён элементов формы
MoonShine.iterable.reindex(container,itemSelector = '.item')MoonShine.iterable.reindex(container,itemSelector = '.item')
Обработка ответа
MoonShine позволяет интегрироваться в процесс выполнения асинхронных запросов в JS, указывая какая функция выполнится перед запросом и после получения ответа.
ActionButton, FormBuilder, TableBuilder, Field и другие компоненты, реализующие интерфейс HasAsyncContract в async методах, также содержат параметр callback.
За передачу параметра callback отвечает класс AsyncCallback. Давайте рассмотрим пример для ActionButton:
Переопределение обработки ответа
С помощью параметра responseHandler можно полностью переопределить поведение обработки ответа.
В случае переопределения вызов событий, обработку ошибок, уведомлений и прочее вы берете на себя.
ActionButton::make()->method('myMethod',callback: AsyncCallback::with(responseHandler: 'myResponseHandler'))ActionButton::make()->method('myMethod',callback: AsyncCallback::with(responseHandler: 'myResponseHandler'))
responseHandler полностью берет обработку ответа на себя, исключая поведение по умолчанию
При клике на кнопку отправим запрос в метод myMethod и при ответе вызовем функцию myResponseHandler.
Следующий шаг - необходимо объявить эту функцию в JS через глобальный класс MoonShine:
document.addEventListener("moonshine:init", () => {MoonShine.onCallback('myResponseHandler', function(response, element, events, component) {console.log('myResponseHandler', response, element, events, component)})})document.addEventListener("moonshine:init", () => {MoonShine.onCallback('myResponseHandler', function(response, element, events, component) {console.log('myResponseHandler', response, element, events, component)})})
response- объект ответа,element- HTML элемент, в данном случае кнопкаActionButton,events- события которые будут вызваны,component- компонент Alpine.js.
Вызов до запроса
Далее рассмотрим пример с функцией до вызова запроса через параметр beforeRequest:
ActionButton::make()->method('myMethod',callback: AsyncCallback::with(beforeRequest: 'myBeforeRequest'))ActionButton::make()->method('myMethod',callback: AsyncCallback::with(beforeRequest: 'myBeforeRequest'))
При нажатии на кнопку перед отправкой запроса в метод myMethod будет выполнена функция myBeforeRequest.
Следующий шаг - необходимо объявить эту функцию в JS через глобальный класс MoonShine:
document.addEventListener("moonshine:init", () => {MoonShine.onCallback('myBeforeRequest', function(element, component) {console.log('myBeforeRequest', element, component)})})document.addEventListener("moonshine:init", () => {MoonShine.onCallback('myBeforeRequest', function(element, component) {console.log('myBeforeRequest', element, component)})})
element- HTML элемент, в данном случае кнопкаActionButton,component- компонент Alpine.js.
Вызов после успешного ответа
Далее рассмотрим пример с параметром afterResponse, принимающим наименование функции, которая будет вызвана после успешного ответа:
ActionButton::make()->method('myMethod',callback: AsyncCallback::with(afterResponse: 'myAfterResponse'))ActionButton::make()->method('myMethod',callback: AsyncCallback::with(afterResponse: 'myAfterResponse'))
Если вы указали responseHandler, то поведение обратки ответа принимает ваша функция и afterResponse вызван не будет.
При клике на кнопку будет отправлен запрос в метод myMethod и в случае успеха будет вызвана функция myAfterResponse.
Следующий шаг - необходимо объявить эту функцию в JS через глобальный класс MoonShine:
document.addEventListener("moonshine:init", () => {MoonShine.onCallback('myAfterResponse', function(data, messageType, component) {console.log('myAfterResponse', data, messageType, component)})})document.addEventListener("moonshine:init", () => {MoonShine.onCallback('myAfterResponse', function(data, messageType, component) {console.log('myAfterResponse', data, messageType, component)})})
data- JSON ответ,messageType-ToastType,component- компонент Alpine.js.