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
You can call window.customConfirm
to get boolean true
or false
like below.
isConfirmed = await window.customConfirm({...});
console.log(isConfirmed) // 👈 true or false
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>
Step 1: Define window.customConfirm
and assign it to @click
on button
In script.js
window.customConfirm = () => {
console.log('customConfirm called');
};
In index.html
<button
x-data
@click="
window.customConfirm();
"
>
open dialog
</button>
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>
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;
},
});
});
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>
And then,
script.js
window.customConfirm = () => {
Alpine.store('confirmModal').toggle();
};
Now we can toggle modal like below.
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;
},
});
});
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>
...
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();
};
And in index.html
<button
x-data
@click="
isConfirmed = await window.customConfirm();
console.log(isConfirmed);
"
>
open dialog
</button>
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() {},
});
});
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);
};
});
};
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)
You could check the "dialog" component in Vimesh Headless UI ( github.com/vimeshjs/vimesh-headless ), an alpinejs alternative of Tailwind Headless UI.