Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Vuetify Confirm Dialog component that can be used locally or globally
<template>
<v-dialog v-model="dialog" :max-width="options.width" :style="{ zIndex: options.zIndex }" @keydown.esc="cancel">
<v-card>
<v-toolbar dark :color="options.color" dense flat>
<v-toolbar-title class="white--text">{{ title }}</v-toolbar-title>
</v-toolbar>
<v-card-text v-show="!!message" class="pa-4">{{ message }}</v-card-text>
<v-card-actions class="pt-0">
<v-spacer></v-spacer>
<v-btn color="primary darken-1" text @click.native="agree">Yes</v-btn>
<v-btn color="grey" text @click.native="cancel">Cancel</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
/**
* Vuetify Confirm Dialog component
*
* Insert component where you want to use it:
* <confirm ref="confirm"></confirm>
*
* Call it:
* this.$refs.confirm.open('Delete', 'Are you sure?', { color: 'red' }).then((confirm) => {})
* Or use await:
* if (await this.$refs.confirm.open('Delete', 'Are you sure?', { color: 'red' })) {
* // yes
* }
* else {
* // cancel
* }
*
* Alternatively you can place it in main App component and access it globally via this.$root.$confirm
* <template>
* <v-app>
* ...
* <confirm ref="confirm"></confirm>
* </v-app>
* </template>
*
* mounted() {
* this.$root.$confirm = this.$refs.confirm.open
* }
*/
export default {
data: () => ({
dialog: false,
resolve: null,
reject: null,
message: null,
title: null,
options: {
color: 'primary',
width: 290,
zIndex: 200
}
}),
methods: {
open(title, message, options) {
this.dialog = true
this.title = title
this.message = message
this.options = Object.assign(this.options, options)
return new Promise((resolve, reject) => {
this.resolve = resolve
this.reject = reject
})
},
agree() {
this.resolve(true)
this.dialog = false
},
cancel() {
this.resolve(false)
this.dialog = false
}
}
}
</script>
@Izopi4a

This comment has been minimized.

Copy link

@Izopi4a Izopi4a commented Mar 12, 2018

awesome, thanks

@joaovieirabr

This comment has been minimized.

Copy link

@joaovieirabr joaovieirabr commented Mar 24, 2018

Thanks. Helped me a lot.

@Dacredible

This comment has been minimized.

Copy link

@Dacredible Dacredible commented Apr 10, 2018

Nice work

@PatrLind

This comment has been minimized.

Copy link

@PatrLind PatrLind commented Apr 17, 2018

Perfect!

@mohammed-io

This comment has been minimized.

Copy link

@mohammed-io mohammed-io commented Apr 21, 2018

It's awesome!

But it has a little issue, it doesn't trigger reject at all

cancel() {

    this.resolve(false); <---- it should be reject

    this.dialog = false;

}
@eolant

This comment has been minimized.

Copy link
Owner Author

@eolant eolant commented Apr 22, 2018

@mohammed-io thank you for your comment, but I would disagree on this as reject should handle errors that occur in the Promise handler. I think confirm dialog should resolve to true or false value as standard JS confirm function does rather than having false case in an error callback.

@Christilut

This comment has been minimized.

Copy link

@Christilut Christilut commented May 10, 2018

Thanks for this! I prefer the reject way also, but it's personal I guess :)

@karljacques

This comment has been minimized.

Copy link

@karljacques karljacques commented May 12, 2018

Thanks, this was helpful.

In the description to use this globally, I think this part:

mounted() {
   *   this.$root.$confirm = this.$refs.confirm.open;`

Should actually be this:

mounted() {
   *   this.$root.$confirm = this.$refs.confirm;
@eolant

This comment has been minimized.

Copy link
Owner Author

@eolant eolant commented May 13, 2018

@karljacques Thanks for the comment :) It's probably a matter of preference. Since it has only one method I thought it's a bit easier to call it with just this.$root.$confirm(title, message, options).then(confirm => {})

@ibanjo

This comment has been minimized.

Copy link

@ibanjo ibanjo commented Jun 4, 2018

Great one, really!

@nizantz

This comment has been minimized.

Copy link

@nizantz nizantz commented Jun 9, 2018

Awesome. This helped!!

@jshowatt

This comment has been minimized.

Copy link

@jshowatt jshowatt commented Jul 23, 2018

This is great! Thanks for sharing.

@Peraluna

This comment has been minimized.

Copy link

@Peraluna Peraluna commented Aug 3, 2018

Very good!!! Simple and it works!

@sergiofactvs

This comment has been minimized.

Copy link

@sergiofactvs sergiofactvs commented Oct 7, 2018

Guys, you must setup the z-index prop, for confirm in dialogs forms.

@eolant

This comment has been minimized.

Copy link
Owner Author

@eolant eolant commented Nov 8, 2018

@sergiofactvs Sorry, took me a while to get this sorted. I've added a parameter for z-index.

@tripflex

This comment has been minimized.

Copy link

@tripflex tripflex commented Dec 8, 2018

@eolant thanks this is awesome! Created a component from this I called DialogLoader that I use along with this one, to handle showing a loader and snackbar when performing actions:

https://gist.github.com/tripflex/19760cdb19260f95412dc751f9c37e79

@circlefind

This comment has been minimized.

Copy link

@circlefind circlefind commented Jan 12, 2019

Error use await:

if (await this.$refs.confirm.open('Delete', 'Are you sure?', { color: 'red' })) {
}
else {
}

@eolant

This comment has been minimized.

Copy link
Owner Author

@eolant eolant commented Jan 14, 2019

@circlefind thanks for your comment. Can you please be more specific in what kind of error you found and what versions of libraries are you using? One suggestion though is to make sure that function you're calling this method in have async keyword e.g. async mounted().

@GibranLara

This comment has been minimized.

Copy link

@GibranLara GibranLara commented Jan 21, 2019

Thank you!

@claudiojuniorfabiao

This comment has been minimized.

Copy link

@claudiojuniorfabiao claudiojuniorfabiao commented Mar 18, 2019

Muito bom, obrigado pelo exemplo!

@lemon5920

This comment has been minimized.

Copy link

@lemon5920 lemon5920 commented Mar 22, 2019

Awesome! It's very helpful.

@sergevdz

This comment has been minimized.

Copy link

@sergevdz sergevdz commented Apr 7, 2019

Thank you so much!!!

@Yogi-p

This comment has been minimized.

Copy link

@Yogi-p Yogi-p commented May 2, 2019

getting error as "Property 'open' does not exist on type 'Vue | Element | Vue[] | Element[]'.
Property 'open' does not exist on type 'Vue'"

@eolant

This comment has been minimized.

Copy link
Owner Author

@eolant eolant commented May 6, 2019

@Yogi-p thanks for your comment, I can't seem to be able to reproduce your issue. Can you please double check if you using it according to the instructions provided and if you still have the error, can you please set up a sandbox for me to check?

@cdecinkoKnight

This comment has been minimized.

Copy link

@cdecinkoKnight cdecinkoKnight commented May 31, 2019

How would I call this from a delete v-icon on a row of data in a Vuetify data table?

@ukadawit

This comment has been minimized.

Copy link

@ukadawit ukadawit commented Jun 7, 2019

Thanks, it worked for me...

@martinkrulltott

This comment has been minimized.

Copy link

@martinkrulltott martinkrulltott commented Jun 11, 2019

Thanks, love this solution. Should really be part of the official docs.

@eolant

This comment has been minimized.

Copy link
Owner Author

@eolant eolant commented Jun 19, 2019

@cdecinkoKnight in theory you would bind v-icon's @click event to a method and then call dialog from it as shown in examples in the comments.

@fabio-rsys

This comment has been minimized.

Copy link

@fabio-rsys fabio-rsys commented Oct 8, 2019

I'm trying to use this on Nuxt,
imported the component and placed it on my default layout, but no success, getting the same error as @Yogi-p . Anyone ever did it?

@eolant

This comment has been minimized.

Copy link
Owner Author

@eolant eolant commented Oct 9, 2019

@fabionfd as this is not in TS, you might struggle a bit to converting it to TS. I didn't make it work all the way, but here is an example how to get rid of this error: https://codesandbox.io/embed/example-options-api-minimal-xt21i

@rom-23

This comment has been minimized.

Copy link

@rom-23 rom-23 commented Oct 10, 2019

Thanks , good job ;)

@edrichhans

This comment has been minimized.

Copy link

@edrichhans edrichhans commented Oct 19, 2019

Thank you for this!! <3 <3 <3

@i-defranca

This comment has been minimized.

Copy link

@i-defranca i-defranca commented Nov 1, 2019

Maybe it would be useful to add an option without the cancel button, when it's a simple message with an ok button. But I think for most users the vuetify snackbar it's enough.
And thank you, of course! I was having trouble making a global component and this code helped me :)

@eolant

This comment has been minimized.

Copy link
Owner Author

@eolant eolant commented Nov 1, 2019

@i-defranca since this is not a library you can do any modifications you want :)

@mazdak78

This comment has been minimized.

Copy link

@mazdak78 mazdak78 commented Nov 27, 2019

Fantastic. Thank you.

@Kaspler

This comment has been minimized.

Copy link

@Kaspler Kaspler commented Dec 4, 2019

Small change to return "false" when the dialog is closed by clicking outside:

<template>
  <v-dialog
    v-model="show"
    :max-width="options.width"
    :style="{ zIndex: options.zIndex }"
    @keydown.esc="cancel"
  >
    <v-card>
      <v-toolbar :color="options.color" dark dense flat>
        <v-toolbar-title class="white--text">{{ title }}</v-toolbar-title>
      </v-toolbar>
      <v-card-text v-show="!!message" class="pa-4">{{ message }}</v-card-text>
      <v-card-actions class="pt-0">
        <v-spacer></v-spacer>
        <v-btn @click.native="agree" color="primary darken-1" text>Yes</v-btn>
        <v-btn @click.native="cancel" color="grey" text>Cancel</v-btn>
      </v-card-actions>
    </v-card>
  </v-dialog>
</template>

<script>
/**
 * Vuetify Confirm Dialog component
 *
 * Insert component where you want to use it:
 * <confirm ref="confirm"></confirm>
 *
 * Call it:
 * this.$refs.confirm.open('Delete', 'Are you sure?', { color: 'red' }).then((confirm) => {})
 * Or use await:
 * if (await this.$refs.confirm.open('Delete', 'Are you sure?', { color: 'red' })) {
 *   // yes
 * }
 * else {
 *   // cancel
 * }
 *
 * Alternatively you can place it in main App component and access it globally via this.$root.$confirm
 * <template>
 *   <v-app>
 *     ...
 *     <confirm ref="confirm"></confirm>
 *   </v-app>
 * </template>
 *
 * mounted() {
 *   this.$root.$confirm = this.$refs.confirm.open
 * }
 */
export default {
  data: () => ({
    dialog: false,
    resolve: null,
    reject: null,
    message: null,
    title: null,
    options: {
      color: 'primary',
      width: 290,
      zIndex: 200
    }
  }),
  computed: {
    show: {
      get() {
        return this.dialog
      },
      set(value) {
        this.dialog = value
        if (value === false) {
          this.cancel()
        }
      }
    }
  },
  methods: {
    open(title, message, options) {
      this.dialog = true
      this.title = title
      this.message = message
      this.options = Object.assign(this.options, options)
      return new Promise((resolve, reject) => {
        this.resolve = resolve
        this.reject = reject
      })
    },
    agree() {
      this.resolve(true)
      this.dialog = false
    },
    cancel() {
      this.resolve(false)
      this.dialog = false
    }
  }
}
</script>

Now, the dialog itself is bound to a computed value "show", which in turn is bound to the "dialog" property. When the value of "show "is set to false - it will trigger the "close()" method.

@NavidMitchell

This comment has been minimized.

Copy link

@NavidMitchell NavidMitchell commented Feb 6, 2020

Nice Work!

@guilherme-miranda

This comment has been minimized.

Copy link

@guilherme-miranda guilherme-miranda commented Apr 19, 2020

Tks!

@shehanrangana

This comment has been minimized.

Copy link

@shehanrangana shehanrangana commented Apr 22, 2020

Nice work !

@TimVanHerwijnen

This comment has been minimized.

Copy link

@TimVanHerwijnen TimVanHerwijnen commented Apr 28, 2020

Learned a lot from this thank you!

@bekaku

This comment has been minimized.

Copy link

@bekaku bekaku commented May 22, 2020

composition-api Version

Confirm.vue

import { defineComponent, reactive, toRefs } from "@vue/composition-api";
export default defineComponent({
  setup() {
    const state = reactive({
      dialog: false,
      resolve: null,
      reject: null,
      message: null,
      title: null,
      options: {
        color: "primary",
        width: 290,
        zIndex: 200
      }
    });
    const open = (title, message, options) => {
      state.dialog = true;
      state.title = title;
      state.message = message;
      state.options = Object.assign(state.options, options);
      return new Promise((resolve, reject) => {
        state.resolve = resolve;
        state.reject = reject;
      });
    };

    const agree = () => {
      console.log("confirm");
      state.resolve(true);
      state.dialog = false;
    };

    const cancel = () => {
      console.log("cancle");
      state.resolve(false);
      state.dialog = false;
    };

    return {
      ...toRefs(state),
      open,
      agree,
      cancel
    };
  }
});
</script>

call it :
Page.vue

<template>
     <div>
              <v-btn
                icon
                color="red"
                @click="openConfirm "
              >
                <v-icon>mdi-trash-can-outline</v-icon>
              </v-btn>
           <confirm ref="veeConfirmRef"></confirm>
     </div>
</template>
<script>
import { onMounted } from "@vue/composition-api";
export default {
  components: {
    Confirm: () => import("@/components/Confirm")
  },
 setup(props, { refs }) {
 
    onMounted(() => {
      console.log('page onMounted')
    });
  const openConfirm = ()=> {
       refs.veeConfirmRef
        .open('Delete', 'Are you sure?', { color: "error" })
        .then(confirm => {
          console.log("onBeforeDeleteItem confirm : " + confirm);
        });

    return{
       openConfirm 
    }
  }
};
</script>
@twoco

This comment has been minimized.

Copy link

@twoco twoco commented May 25, 2020

For TypeScript to prevent type errors.

this.$root.$refs.confirm = this.$refs.confirm;
// or ... to avoid type errors.
this.$root.$data.confirm = this.$refs.confirm;

Maybe this is the more correct way, isn't? I'm new in Vue(tify). But the $root $refs exists as type.

(property) Vue.$refs: {
    [key: string]: Vue | Element | Vue[] | Element[];
}

You could also use a shared class ...

@eliyahuKriel

This comment has been minimized.

Copy link

@eliyahuKriel eliyahuKriel commented Jun 8, 2020

thank you very much! i search for this all over the web!
how can i call this.$root.$confirm from VUEX ? i want to active the modal from my store.. is a way to get there through JS file?

@eolant

This comment has been minimized.

Copy link
Owner Author

@eolant eolant commented Jun 8, 2020

@eliyahuKriel you can try importing app instance.

@eliyahuKriel

This comment has been minimized.

Copy link

@eliyahuKriel eliyahuKriel commented Jun 8, 2020

thanks, i am tring but the modal just dont open..
export const SET_CONFIRM_MODAL = (state, modalData) => { App.components.Confirm.methods.openModal(modalData) };

should i access other way to the openModal method?

@eolant

This comment has been minimized.

Copy link
Owner Author

@eolant eolant commented Jun 8, 2020

@eliyahuKriel hmm, not sure about your code, but you should use an instance app:

export const app = new Vue({
  router,
  store,
  render: h => h(require('$comp/App'))
}).$mount('#app');
@eliyahuKriel

This comment has been minimized.

Copy link

@eliyahuKriel eliyahuKriel commented Jun 8, 2020

God bless you my friend, you have helped me very much

@jmacias54

This comment has been minimized.

Copy link

@jmacias54 jmacias54 commented Jun 16, 2020

great ...!!!

@Cerceis

This comment has been minimized.

Copy link

@Cerceis Cerceis commented Nov 18, 2020

Thanks, this was helpful.

In the description to use this globally, I think this part:

mounted() {
   *   this.$root.$confirm = this.$refs.confirm.open;`

Should actually be this:

mounted() {
   *   this.$root.$confirm = this.$refs.confirm;

Along with this post.
When using it as global component(registered in App.vue)
I need to use *await this.$root.$confirm.open('Delete', 'Are you sure?', { color: 'red' })
instead of *await this.$refs.$confirm.open('Delete', 'Are you sure?', { color: 'red' })

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.