Basics
Alpine.js
is integrated into MoonShine
"out of the box" and provides a declarative approach to creating dynamic behavior right in the HTML markup.
This allows you to easily add such features as:
- Dynamic hiding/showing of elements
- Event handling
- Reactivity
- Asynchronous requests
- Animations and transitions
Thanks to its lightweight nature and simple syntax, Alpine.js
is well-suited for admin panel tasks without weighing down your application.
Although Alpine.js
is the recommended solution for MoonShine, you are not limited in your choice of JavaScript tools or using vanilla js.
We recommend familiarizing yourself with Alpine.js
Creating a component
Let's try to create our own component.
php artisan moonshine:component MyComponent
We'll leave the path as suggested by MoonShine
- /resources/views/admin/components/my-component.blade.php
.
Inside, we'll add x-data
with the name of our component, thereby indicating that the area inside is an Alpine.js
component.
<div x-data="myComponent"></div>
Next, we'll create a script where we will later implement the logic of the Alpine.js
component.
<script>document.addEventListener("alpine:init", () => { Alpine.data("myComponent", () => ({ init() { }, }))})</script>
For clarity, we have shown you the script right in blade
,
but we recommend placing components in separate js
files and including them via AssetManager.
Alpine.js is already installed and running (window.Alpine); reinitializing will cause an error.
Events
With js
events, you can easily interact with MoonShine
! Update forms, tables, areas, trigger modal windows, reset forms, and much more.
You can also create your own events in js
.
Default events
In the MoonShine
admin panel, several standard events are defined, the names of which can be conveniently obtained through the enum
JsEvent
, but you can also call them from js
.
-
fragment_updated:{componentName}
(JsEvent::FRAGMENT_UPDATED
) - fragment update, -
table_updated:{componentName}
(JsEvent::TABLE_UPDATED
) - table update, -
table_reindex:{componentName}
(JsEvent::TABLE_REINDEX
) - table index update on sorting, -
table_row_updated:{componentName}-{row-id}
(JsEvent::TABLE_ROW_UPDATED
) - row update in the table, -
cards_updated:{componentName}
(JsEvent::CARDS_UPDATED
) - card list update, -
form_reset:{componentName}
(JsEvent::FORM_RESET
) - form reset, -
form_submit:{componentName}
(JsEvent::FORM_SUBMIT
) - form submission, -
modal_toggled:{componentName}
(JsEvent::MODAL_TOGGLED
) - opening/closing modal window, -
off_canvas_toggled:{componentName}
(JsEvent::OFF_CANVAS_TOGGLED
) - opening/closingOffCanvas
, -
popover_toggled:{componentName}
(JsEvent::POPOVER_TOGGLED
) - opening/closingOffCanvas
, -
toast:{componentName}
(JsEvent::TOAST
) - triggering Toast, -
show_when_refresh:{componentName}
(JsEvent::SHOW_WHEN_REFRESH
) - refreshshowWhen
states,
Calling events
Let's recall how this is done on the backend
side:
Modal::make( 'Title', 'Content',)->name('my-modal'), ActionButton::make( 'Show modal window', '/endpoint') ->async(events: [AlpineJs::event(JsEvent::MODAL_TOGGLED, 'my-modal')])
But this section is dedicated to frontend
and events can be called using native javascript methods:
document.addEventListener("DOMContentLoaded", () => { this.dispatchEvent(new CustomEvent("modal_toggled:my-modal"))})
Or using the magic method $dispatch()
from Alpine.js
:
this.$dispatch('modal_toggled:my-modal')
Calling events through Response
In MoonShine, you can return events in MoonShineJsonResponse, which will then be triggered.
To do this, you need to use the events()
method.
events(array $events)
-
$events
- an array of events to be triggered.
use MoonShine\Support\Enums\JsEvent;use MoonShine\Laravel\Http\Responses\MoonShineJsonResponse;use MoonShine\Support\AlpineJs; //... return MoonShineJsonResponse::make() ->events([ AlpineJs::event(JsEvent::TABLE_UPDATED, 'index'), ]);
Blade directive
Blade directives are used for quickly declaring events for components.
@defineEvent
A directive for conveniently declaring an event in html
.
@defineEvent(string|JsEvent $event, ?string $name = null, ?string $call = null, array $params = [])
-
$event
- the event, -
$name
- the component name, -
$call
- the callback function, -
$params
- event parameters.
<div x-data="myComponent"> // @table-updated-index.window="asyncRequest" @defineEvent('table-updated', 'index', 'asyncRequest') ></div>
@defineEventWhen
With a condition of whether it will be added or not.
@defineEventWhen(mixed $condition, string|JsEvent $event, ?string $name = null, ?string $call = null, array $params = [])
-
$condition
- condition, -
$event
- the event, -
$name
- the component name, -
$call
- the callback function, -
$params
- event parameters.
<div x-data="myComponent"> // @table-updated-index.window="asyncRequest" @defineEventWhen(true, 'table-updated', 'index', 'asyncRequest') ></div>
Helper class AlpineJs for event formation.
AlpineJs::event()
AlpineJs::event(string|JsEvent $event, ?string $name = null, array $params = [])
-
$event
- the event, -
$name
- the component name, -
$params
- event parameters.
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 = [])
-
$event
- the event, -
$name
- the component name, -
$call
- the callback function, -
$params
- event parameters.
FormBuilder::make('/crud/update') ->name('main-form') ->customAttributes([ // @form-reset-main-form.window="formReset" AlpineJs::eventBlade(JsEvent::FORM_RESET, 'main-form') => 'formReset', ]);
Global class
We provide a global class MoonShine
for convenient interaction on the client side.
Request
MoonShine.request(ctx, '/url', method = 'get', body = {}, headers = {}, data = {})
Toast
MoonShine.ui.toast('Hello world', 'success')
Modal
Open/Close Modal
MoonShine.ui.toggleModal('modal-name')
OffCanvas
Open/Close OffCanvas
MoonShine.ui.toggleOffCanvas('canvas-name')
Iterable
Reorder
MoonShine.iterable.sortable( container, url, group, events, attributes = { handle: '.handle' }, function(evt) { // })
Reindexing of form element names
MoonShine.iterable.reindex( container, itemSelector = '.item')
Response handling
MoonShine
allows integration into the process of executing asynchronous requests in js
, specifying which function will run before the request and after receiving the response.
ActionButton
, FormBuilder
, TableBuilder
, Field
, and other components implementing the HasAsyncContract
interface in async
methods also contain the callback
parameter.
The class AsyncCallback
is responsible for passing the callback
parameter. Let's consider an example for ActionButton
:
Overriding response handling
With the responseHandler
parameter, you can completely redefine the behavior of response handling. In the case of overriding, you take care of event calls, error handling, notifications, and more.
ActionButton::make()->method('myMethod', callback: AsyncCallback::with(responseHandler: 'myResponseHandler'));
responseHandler
takes full control of response handling, excluding the default behavior.
When clicking the button, a request will be sent to the method myMethod
, and upon response, the function myResponseHandler
will be called.
The next step is to declare this function in js
through the global class MoonShine
:
document.addEventListener("moonshine:init", () => { MoonShine.onCallback('myResponseHandler', function(response, element, events, component) { console.log('myResponseHandler', response, element, events, component) })})
-
response
- the response object, -
element
- thehtml
element, in this case, theActionButton
, -
events
- the events that will be triggered, -
component
- theAlpine.js
component.
Call before request
Next, let's look at an example with the function before the request call through the beforeRequest
parameter:
ActionButton::make()->method('myMethod', callback: AsyncCallback::with(beforeRequest: 'myBeforeRequest'));
When the button is pressed, the function myBeforeRequest
will be executed before sending the request to the method myMethod
.
The next step is to declare this function in js
through the global class MoonShine
:
document.addEventListener("moonshine:init", () => { MoonShine.onCallback('myBeforeRequest', function(element, component) { console.log('myBeforeRequest', element, component) })})
-
element
- thehtml
element, in this case, theActionButton
, -
component
- theAlpine.js
component.
Call after successful response
Next, let's look at an example with the afterResponse
parameter, which takes the name of the function to be called after a successful response:
ActionButton::make()->method('myMethod', callback: AsyncCallback::with(afterResponse: 'myAfterResponse'));
If you specified responseHandler
, then the response handling behavior takes your function, and afterResponse
will not be called.
When clicking the button, a request will be sent to the method myMethod
, and in case of success, the function myAfterResponse
will be called.
The next step is to declare this function in js
through the global class MoonShine
:
document.addEventListener("moonshine:init", () => { MoonShine.onCallback('myAfterResponse', function(data, messageType, component) { console.log('myAfterResponse', data, messageType, component) })})
-
data
- thejson
response, -
messageType
-ToastType
, -
component
- theAlpine.js
component.