Table of Contents & Menu
Navigation

Open a Modal from Outside Alpine Components

Learn how to open a modal dialog from anywhere on your page, even outside its Alpine.js component.

As of Hyvä release 1.1.18, you can open modal dialogs by dispatching the hyva-modal-show event.

From 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:

window.dispatchEvent(
  new CustomEvent('hyva-modal-show', {detail: {dialog: 'my-modal'}})
)

Important: Hyvä >= 1.1.18

The hyva-modal-show event listener requires hyva-themes/magento2-theme-module version 1.1.18 or higher. If your module depends on this event, ensure you add the following to its composer.json:

"hyva-themes/magento2-theme-module": "^1.1.18"

For Hyvä <= 1.1.17: Using a Custom Event

For older Hyvä versions, the most direct approach is to use a custom event.

Step 1: Declare the view model:

$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
      );

Step 2: Declare the modal component with an event listener:

<div x-data="hyva.modal()" @open-my-dialog.window="<?= $modal->getShowJs() ?>">
    <?= $modal ?>
</div>

Accessibility: Specifying Focus After Modal Close

Available since Hyvä 1.2.9 / 1.3.5

For improved accessibility, ensure focus returns to the correct element after the modal closes. When dispatching the hyva-modal-show event, you can specify this element using the focusAfterHide property:

{dialog: 'my-modal', focusAfterHide: '#some-selector'}

Method 2: Using a Global Function

Alternatively, you can define a global function to open the modal from anywhere. This involves capturing the Alpine component's scope within a global function during initialization.

Step 1: Declare the view model:

$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
      );

Step 2: Render the modal and define the global function:

<div x-data="{
    ...hyva.modal(),
    init() {
        window.openMyDialog = function (event) {
            this.show('my-dialog', event);
        }.bind(this);
    }
}" x-init="init()">
    <?= $modal ?>
</div>

The init function, declared within x-data and merged with hyva.modal(), captures the component's scope. It's then called via x-init="init()" (automatically handled in Alpine v3).

Once set up, you can open the dialog from anywhere by calling window.openMyDialog().

<div>
    <a onclick="openMyDialog()" class="btn">This trigger can be anywhere</a>
</div>