最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to programmatically launch a Vuetify Dialog and wait for the response - Stack Overflow

programmeradmin6浏览0评论

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
Add a ment  | 

3 Answers 3

Reset to default 11

My 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;
    }
  }
发布评论

评论列表(0)

  1. 暂无评论