Vue & Vuetify Composable Confirm Dialogs
Introduction
Vuetify, a popular Material Design framework for Vue, provides a wide range of UI components, including highly customizable dialogs. This article will guide you through building composable programmatic dialogs using Vue and Vuetify, allowing you to manage dialogs efficiently and flexibly.Creating programmatic dialogs in Vue using Vuetify allows developers to dynamically generate dialogs that enhance the user experience without cluttering the template with numerous dialog components.
Project Setup
## with npm
npm create vuetify@latest
## with yarn
yarn create vuetify
## with pnpm
pnpm create vuetify
## with bun
bun create vuetify
Create the Dialog component
Create a new dialog component in src/components, here I will create a file names Dialog.vue
touch src/dialogs/Dialog.vue
The content of this file is :
<template>
<v-dialog v-model="open" width="50%" persistent max-width="60%">
<v-card :title="title">
<v-card-text>{{ message }}</v-card-text>
<v-card-actions>
<v-btn color="success" @click="submitDialog">{{ submitText }}</v-btn>
<v-btn color="error" @click="cancelDialog">{{ cancelText }}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script setup>
const open = defineModel({ default: false, type: Boolean })
const emits = defineEmits(["submit", "cancel"])
const props = defineProps({
title: {
type: String,
default: () => ("Attention"),
},
message: {
type: String,
default: () => (null),
},
submitText: {
type: String,
default: () => ("Submit"),
},
cancelText: {
type: String,
default: () => ("Cancel"),
}
}
)
const submitDialog = () => {
emits('submit')
open.value=false
}
const cancelDialog = () => {
emits('cancel')
open.value=false
}
</script>
<v-dialog>: A Vuetify component that creates a modal dialog box.persistent: Prevents the user from closing the dialog by clicking outside of it.:title="title": Binds thetitleprop to the card title.<v-btn color="success">: A button with a green color labeled with thesubmitTextprop (default is "Submit").<v-btn color="error">: A button with a red color labeled with thecancelTextprop (default is "Cancel").
And in the script section
const open = defineModel({ default: false, type: Boolean }): This line creates a reactiveopenproperty with a default value offalseand specifies that it should be of typeBoolean. This property controls whether the dialog is visible.const emits = defineEmits(["submit", "cancel"]): Defines events that the component can emit, namely "submit" and "cancel". These events would typically be triggered when the corresponding buttons are clicked, but they are not explicitly emitted in the current code.const props = defineProps({ ... }): Defines the properties (props) that the component accepts:title: AStringprop with a default value of "Attention".message: AStringprop that defaults tonull.submitText: AStringprop with a default value of "Submit".cancelText: AStringprop with a default value of "Cancel".
Create Composable file
mkdir -p src/composables && touch /src/composable/useDialog.js
In the useDialog.js file copy and past this coce :
import { createApp, mergeProps } from 'vue'
import Dialog from '@/components/Dialog.vue'
import vuetify from '@/plugins/vuetify'
export const getVAppRoot = () => document.body
export const createContainer = () => document.createElement('div')
export const useDialog = (props) => {
const rootElement = getVAppRoot()
const container = createContainer()
return new Promise((resolve) => {
const componentApp = createApp(Dialog, mergeProps(
props,
{ modelValue: true },
{
onSubmit() {
resolve(true)
},
onCancel() {
resolve(false)
}
}
))
rootElement.appendChild(container)
componentApp.use(vuetify).mount(container)
})
}
Usage
<template>
<v-container class="fill-height">
<v-btn @click="clickedAsync">Click Async</v-btn>
<v-btn @click="clickedThen">Click Then</v-btn>
</v-container>
</template>
<script setup>
import { useDialog } from '@/composable/useDialog';
const clickedAsync = async () => {
const response = await useDialog({ message: "this works very nice !" });
if (response) {
alert("Dialog confirmed " + response,)
} else {
alert("Dialog canceled " + response, response)
}
}
const clickedThen = () => {
useDialog({ message: "this works very nice !" }).then(response=>{
if (response) {
alert("Dialog confirmed " + response,)
} else {
alert("Dialog canceled " + response, response)
}
})
}
</script>
Result
Github source
Final Words
Dialogs are essential for displaying critical information, confirmations, or forms without leaving the current page. However, managing dialogs can become cumbersome, especially in large-scale applications. Programmatic dialogs help mitigate this by creating dialogs on the fly using JavaScript, making them more manageable and reducing template bloat. Composable dialogs are built using the Composition API in Vue, enabling the encapsulation of dialog-related logic in a reusable and maintainable way.