I'm fairly new to Vue.js and Vuetify (used AngularJS for several years but our pany is switching to Vue.js). What I'm trying to acplish is when the user clicks on a 'sign in' button it does some checks (i.e. username cannot be blank) and launches a Vuetify Dialog to alert the user. I know Vuetify has some built in validation but looking for something a little more robust where I can wait for a reponse (i.e. when I need to wait for something like can I use your history / location).
Basically looking to do:
if (!userName){
userName = await mbox('You must Enter your Username');
return
}
OR
var mProceed = await mbox('Can we use your location for awesome stuff?');
where mbox (a simple messagebox popup box) is a function that returns a promise, programmatically loads a vue ponent, adds it to the dom, and waits for the response.
i.e.
async function mbox (mText) {
// load dialog ponent here and set message passed in
// How Do I load the template / wait for it?
return dialogResult
}
Vue Component would look something like (With button captions and text being variables I pass in to my mbox function):
<template>
<v-layout row justify-center>
<v-dialog
v-model="dialog"
max-width="290"
>
<v-card>
<v-card-title class="headline">Use Google's location service?</v-card-title>
<v-card-text>
Let Google help apps determine location. This means sending anonymous location data to Google, even when no apps are running.
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="green darken-1"
flat="flat"
@click="dialog = false"
>
Disagree
</v-btn>
<v-btn
color="green darken-1"
flat="flat"
@click="dialog = false"
>
Agree
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-layout>
</template>
I'm fine editing the template / adding the script for the vue ponent I'm just not sure how to have it called via a method that returns a promise and waits for the response?
I'm fairly new to Vue.js and Vuetify (used AngularJS for several years but our pany is switching to Vue.js). What I'm trying to acplish is when the user clicks on a 'sign in' button it does some checks (i.e. username cannot be blank) and launches a Vuetify Dialog to alert the user. I know Vuetify has some built in validation but looking for something a little more robust where I can wait for a reponse (i.e. when I need to wait for something like can I use your history / location).
Basically looking to do:
if (!userName){
userName = await mbox('You must Enter your Username');
return
}
OR
var mProceed = await mbox('Can we use your location for awesome stuff?');
where mbox (a simple messagebox popup box) is a function that returns a promise, programmatically loads a vue ponent, adds it to the dom, and waits for the response.
i.e.
async function mbox (mText) {
// load dialog ponent here and set message passed in
// How Do I load the template / wait for it?
return dialogResult
}
Vue Component would look something like (With button captions and text being variables I pass in to my mbox function):
<template>
<v-layout row justify-center>
<v-dialog
v-model="dialog"
max-width="290"
>
<v-card>
<v-card-title class="headline">Use Google's location service?</v-card-title>
<v-card-text>
Let Google help apps determine location. This means sending anonymous location data to Google, even when no apps are running.
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="green darken-1"
flat="flat"
@click="dialog = false"
>
Disagree
</v-btn>
<v-btn
color="green darken-1"
flat="flat"
@click="dialog = false"
>
Agree
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-layout>
</template>
I'm fine editing the template / adding the script for the vue ponent I'm just not sure how to have it called via a method that returns a promise and waits for the response?
Share Improve this question edited May 8, 2019 at 16:46 ScottR asked May 7, 2019 at 15:44 ScottRScottR 2191 gold badge5 silver badges14 bronze badges 2-
1
I simply registered a watcher only once and wrapped with a Promise
await new Promise(resolve => { const stop = this.$watch('dialog', () => resolve(stop())) })
– Moshe Quantz Commented Mar 11, 2021 at 15:27 - If you are looking at this and Migrating from Vue 2 to Vue 3, see this thread about how to migrate it. stackoverflow./questions/76774836/… – ScottR Commented Jul 27, 2023 at 13:27
3 Answers
Reset to default 11My solution.
Page.vue
<template>
<v-container>
<v-layout text-center wrap>
<v-flex xs12>
<v-btn v-on:click="open_dlg">Dlg Wrapper</v-btn>
<dlg-wrapper ref="dlg">
<dlg-frame title="Dialog" message="Message"></dlg-frame>
</dlg-wrapper>
</v-flex>
</v-layout>
</v-container>
</template>
<script>
import DlgWrapper from "@/ponents/DlgWrapper";
import DlgFrame from "@/ponents/DlgFrame";
export default {
data: () => {
return {};
},
methods: {
open_dlg: function(event) {
this.$refs.dlg.open().then(result => {
console.log("Result:", result);
});
}
},
ponents: {
DlgWrapper,
DlgFrame
}
};
</script>
DlgWrapper.vue
<template>
<div>
<v-dialog
v-model="dialog"
persistent
:width="options.width"
v-bind:style="{ zIndex: options.zIndex }"
>
<slot></slot>
</v-dialog>
</div>
</template>
<script>
export default {
name: "dlg-wrapper",
data: () => ({
dialog: false,
options: {
width: 400,
zIndex: 200
},
resolve: null,
reject: null
}),
methods: {
open(options) {
this.dialog = true;
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;
}
},
provide: function() {
return { agree: this.agree, cancel: this.cancel };
}
};
</script>
DlgFrame.vue
<template>
<v-card dark>
<v-card-title v-show="!!title">{{ title }}</v-card-title>
<v-card-text v-show="!!message">{{ message }}</v-card-text>
<v-card-actions>
<v-btn @click="agree">OK</v-btn>
<v-btn @click="cancel">NO</v-btn>
</v-card-actions>
</v-card>
</template>
<script>
export default {
name: "dlg-frame",
props: ["title", "message"],
data: () => ({}),
inject: ["agree", "cancel"],
methods: {}
};
</script>
Good luck!
Ended up solving it this way:
I have a method called mbox (returns a promise) that creates a an instance of my ponent, adds it to the DOM, and then watches a property on that ponent to know which option the user chose. Resolves the promise once the user has chosen an option
My mbox method:
import MBOX from './ponents/mbox.vue';
import _Vue from 'vue';
export default {
mbox(mText, mTitle, mBtnCap1, mBtnCap2, mBtnCap3){
return new Promise(async (resolve, reject) => {
if (!mTitle){
mTitle = 'My Title';
}
if (!mBtnCap1){
mBtnCap1 = 'OK';
}
// I'm bining a bunch of stuff to make this work.
// First, create the vue ponent
var mboxInstance = _Vue.extend(MBOX); // mbox ponent, imported at top of Sublib
var oComponent = new mboxInstance({
propsData: {
msg: mText,
title: mTitle,
btnCap1: mBtnCap1,
btnCap2: mBtnCap2,
btnCap3: mBtnCap3,
retval: 0
}
}).$mount();
// now add it to the DOM
var oEl = document.getElementById('app').appendChild(oComponent.$el);
// NOTE: couldn't get it to work without adding a 'button' to activate it
// progrmatically click it and make the button invisible
// document.getElementById('mbox_btn_launch').click();
var oLuanchBtn = document.getElementById('mbox_btn_launch');
oLuanchBtn.style.visibility = 'hidden';
oLuanchBtn.click();
// Add a listener so we can get the value and return it
oComponent.$watch('retval', function(newVal, oldVal){
// this is triggered when they chose an option
// Delete the ponent / element now that I'm done
oEl.remove();
resolve(Number(newVal));
})
}); // promise
}, // mbox
}
And My MBOX ponent:
<template>
<v-dialog max-width="290" persistent v-if="showMbox">
<template v-slot:activator="{on}">
<v-btn id="mbox_btn_launch" v-on="on">
Test
</v-btn>
</template>
<v-card>
<v-card-title>{{title}}</v-card-title>
<v-card-text>{{msg}}</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn v-if="btnCap1" @click="btnClicked('1')">{{btnCap1}}</v-btn>
<v-btn v-if="btnCap2" @click="btnClicked('2')">{{btnCap2}}</v-btn>
<v-btn v-if="btnCap3" @click="btnClicked('3')">{{btnCap3}}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
export default {
name: 'mbox',
data: () => ({
showMbox: true
}),
props: [
// these can be passed in, the 'data' stuff can't
'msg',
'title',
'btnCap1',
'btnCap2',
'btnCap3',
'retval' // watches this so we know when they clicked something
],
created(){
this.showMbox = true;
},
methods: {
btnClicked: function(mBtnClicked){
// mBtnClicked = Char. Has to be a character in order for it to pass it in. Not sure why, numerics didn't work
mBtnClicked = Number(mBtnClicked);
this.retval = mBtnClicked; // watcher in Sublib will detect this value has changed
this.showMbox = false;
} // btnClicked
} // methods
} // export default
</script>
<style scoped>
</style>
I can then call it like so:
var mChoice = await mbox('What do you want to do?', 'Title', 'Option 1', 'Option 2');
or for a simple 'validation' prompt:
if (!userName){
mbox('You must enter a username');
return;
}
//exit page
beforeRouteLeave (to, from, next){
if(!this.redirect.hasConfirmed){
let originalJson = JSON.stringify(this.original);
let modifiedJson = JSON.stringify(this.configs);
if(originalJson.localeCompare(modifiedJson) != 0){
this.redirect.path = to.fullPath;
this.dialogs.save.show = true;
}else{
next();
}
}else{
this.redirect.hasConfirmed = false;
next();
}
}
methods: {
//close track dialog
closeDialog(params){
this.dialogs.save.show = false;
if(params.action == 'submit'){
this.save();
this.redirect.hasConfirmed = true;
this.$router.push({path: this.redirect.path});
}else if(params.action == 'ignore'){
this.redirect.hasConfirmed = true;
this.$router.push({path: this.redirect.path});
}else if(params.action == 'cancel'){
return;
}
}