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.