DEV Community

Kohhee Peace
Kohhee Peace

Posted on • Updated on

Alpinejs global modal

Intro

This is a tutorial for creating a global modal equivalent to window.confirm by Alpinejs.

A repositories of this code can be found below link.

https://github.com/kohheepeace/alpinejs-window-confirm

demo gif

You can call window.customConfirm to get boolean true or false like below.

  isConfirmed = await window.customConfirm({...});
  console.log(isConfirmed) // πŸ‘ˆ true or false
Enter fullscreen mode Exit fullscreen mode

Step 0: Setup project

Please create Static HTML project at https://stackblitz.com/

We, only use two files

  • index.html
  • script.js

In index.html, add Alpine.js cdn

<html>
  <head>
    <meta charset="UTF-8" />
    <!-- Custom script -->
    <script src="script.js"></script>

    <!-- πŸ‘‡ Alpine.js from cdn -->
    <script
      defer
      src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"
    ></script>
  </head>
  <body>
    ...
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Step 1: Define window.customConfirm and assign it to @click on button

In script.js

window.customConfirm = () => {
  console.log('customConfirm called');
};
Enter fullscreen mode Exit fullscreen mode

In index.html

<button
  x-data
  @click="
    window.customConfirm();
  "
>
  open dialog
</button>
Enter fullscreen mode Exit fullscreen mode

Now, we defined window.customConfirm function and can call it from button by using Alpinejs.

Step 2: Add Modal

In this step, we're going to add modal.

Please modify index.html like below.

index.html

<body>
   <!-- Mimic nested div -->
    <div>
      <div>
        <button x-data @click="window.customConfirm();">open dialog</button>
      </div>
    </div>

    <!-- Modal -->
    <div style="background: pink; border: solid; padding: 40px">
      <h1>Modal Title</h1>
      <p>Modal message</p>
      <button>Cancel</button>
      <button>OK</button>
    </div>
</body>
Enter fullscreen mode Exit fullscreen mode

Step 3: Add Alpine Store

To handle modal's open state, let's add Alpine store.

script.js

document.addEventListener('alpine:init', () => {
  Alpine.store('confirmModal', {
    open: false,
    toggle() {
      this.open = !this.open;
    },
  });
});
Enter fullscreen mode Exit fullscreen mode

Then in index.html

<body>
  ...
  <!-- Modal -->
  <div x-data x-show="$store.confirmModal.open"> <!-- πŸ‘ˆ Add this line -->
    <div style="background: pink; border: solid; padding: 40px">
      <h1>Modal Title</h1>
      <p>Modal message</p>
      <button>Cancel</button>
      <button>OK</button>
    </div>
  </div>
</body>
Enter fullscreen mode Exit fullscreen mode

And then,
script.js

window.customConfirm = () => {
  Alpine.store('confirmModal').toggle();
};
Enter fullscreen mode Exit fullscreen mode

Now we can toggle modal like below.

demo-step3-1

Step 4: Add function onOk and onCancel

In this step, we're going to add function when modal OK and Cancel is clicked.

script.js

🚫 Note: this code is for showing wrong implementation.

document.addEventListener('alpine:init', () => {
  Alpine.store('confirmModal', {
    open: false,
    toggle() {
      this.open = !this.open;
    },
    onOk() {
      console.log('onOk clicked');
      return true;
    },
    onCancel() {
      console.log('onCancel clicked');
      return false;
    },
  });
});
Enter fullscreen mode Exit fullscreen mode

Then in index.html

...
  <!-- Mimic nested div -->
  <div>
    <div>
      <button
        x-data
        @click="
          isConfirmed = window.customConfirm();
          console.log(isConfirmed);
        "
      >
        open dialog
      </button>
    </div>
  </div>

  <!-- Modal -->
  <div x-data x-show="$store.confirmModal.open">
    <div style="background: pink; border: solid; padding: 40px">
      <h1>Modal Title</h1>
      <p>Modal message</p>
      <button
        @click="
          $store.confirmModal.onCancel();
        "
      >
        Cancel
      </button>
      <button
        @click="
          $store.confirmModal.onOk();
        "
      >
        OK
      </button>
    </div>
  </div>
...
Enter fullscreen mode Exit fullscreen mode

Now, we can assign onOk and onCancel function to buttons in modal.

🚫 But, it does not return boolean true or false value.

In the next step, we will learn how to fix this issue.

Step 5: Promise

To get boolean value from window.customConfirm, we need to use Promise.

For example, let's re write window.customConfirm

script.js

window.customConfirm = () => {
  return new Promise((resolve, reject) => {
    resolve(true);
    // resolve(123) πŸ‘ˆ try returning various values by using `resolve`
    // resolve("hoge")
  });
  // Alpine.store('confirmModal').toggle();
};
Enter fullscreen mode Exit fullscreen mode

And in index.html

<button
  x-data
  @click="
    isConfirmed = await window.customConfirm();
    console.log(isConfirmed);
  "
>
  open dialog
</button>
Enter fullscreen mode Exit fullscreen mode

Alpine @click does not need to declare async, just using await is enough.

You will see now, window.customConfirm returns boolean true value in console.

Step 6: Assign resolve(true) to onOk and onCancel

First we make store onOk and onCancel blank function at begining.

document.addEventListener('alpine:init', () => {
  Alpine.store('confirmModal', {
    open: false,
    toggle() {
      this.open = !this.open;
    },
    onOk() {},
    onCancel() {},
  });
});
Enter fullscreen mode Exit fullscreen mode

Then modify window.customConfirm like below.

window.customConfirm = () => {
  return new Promise((resolve, reject) => {
    const confirmModal = Alpine.store('confirmModal');

    // Open Modal
    confirmModal.open = true;

    // Assign logic: when OK button is clicked, close modal and return true
    confirmModal.onOk = () => {
      confirmModal.open = false;
      resolve(true);
    };

    // Assign logic: when Cancel button is clicked, close modal and return false
    confirmModal.onCancel = () => {
      confirmModal.open = false;
      resolve(false);
    };
  });
};
Enter fullscreen mode Exit fullscreen mode

By doing this, we override onOk and onCancel logic when window.customConfirm is called to return boolean true or false.

Finish!

This is the final code of this tutorial.

https://web-platform-rrgo1m.stackblitz.io

Realworld example with Tailwind

And this is a confirm modal with tailwind which is shown in Intro section.
https://stackblitz.com/edit/web-platform-nwc4sm

Refs

πŸ‘‹ This example is totaly inspired by this great stackoverflow answer
https://stackoverflow.com/a/58891905/6037441

Top comments (1)

Collapse
 
comforx profile image
comforx

You could check the "dialog" component in Vimesh Headless UI ( github.com/vimeshjs/vimesh-headless ), an alpinejs alternative of Tailwind Headless UI.