Opening a modal from outside the Alpine component.
Sometimes you might want to open a dialog from anywhere on the page, from outside the Alpine dialog component.
Using an event
Since release 1.1.18 modal dialogs can be opened by dispatching the event hyva-modal-show
.
From within an Alpine.js component:
<button type="button" aria-haspopup="dialog"
@click="$dispatch('hyva-modal-show', {dialog: 'my-modal'})"
>Open</button>
From anywhere with vanilla JS:
Hyvä >= 1.1.18
The hyva-modal-show
event listener is present since version 1.1.18 of the hyva-themes/magento2-theme-module
package.
If you use rely on this event in a module, be sure to add the appropriate dependency to the modules composer.json
file: "hyva-themes/magento2-theme-module": "^1.1.18"
Using a custom event for Hyvä <= 1.1.17
The most straight forward way to allow opening a modal from anywhere is by using a custom event.
First, declare the view model as usual:
$modal = $viewModels->require(\Hyva\Theme\ViewModel\Modal::class)
->createModal()
->withDialogRefName('my-dialog')
->withContent(<<<EOCONTENT
<div>
<h3>Example dialog that can be opened with an event.</h3>
<pre class="my-4">
// To open from an alpine component use
$dispatch('open-my-dialog')
// or globaly
window.dispatchEvent(new CustomEvent('open-my-dialog'))
</pre>
<button @click="hide" type="button" class="btn">Cancel</button>
</div>
EOCONTENT
);
Then, declare the modal component with an event listener for open-my-dialog
Using a global function
It is also possible to declare a global function that can be called from anywhere.
To do this, the Alpine object scope needs to be captured in a global function, so the dialog reference is available.
This sounds complicated, as almost always is the case with JavaScript scoping rules, but it actually is only a one line function that is declared inside the component initialization callback.
The following example will clarify.
First, declare the view model as usual:
$modal = $viewModels->require(\Hyva\Theme\ViewModel\Modal::class)
->createModal()
->withDialogRefName('my-dialog')
->withContent(<<<EOCONTENT
<div>
<h3>Example dialog that can be opened from anywhere using a function.</h3>
<pre class="my-4">
// To open this dialog call the function
window.openMyDialog();
</pre>
<button @click="hide" type="button" class="btn">Cancel</button>
</div>
EOCONTENT
);
Second, when rendering the modal inside the Alpine.js component, add an init()
function that captures the component scope in a global function.
<div x-data="{
...hyva.modal(),
init() {
window.openMyDialog = function (event) {
this.show('my-dialog', event);
}.bind(this);
}
}" x-init="init()">
<?= $modal ?>
</div>
Here the init
function is declared directly inside the x-data
attribute and merged onto the view model returned by hyva.modal()
using object destructuring but it could be declared just as well in a view model function.
Then, the init function is called with x-init="init()"
(in Alpine v3 that is taken care of automatically).
Now, the dialog can be opened from anywhere on the page by calling window.openMyDialog()
.