【问题标题】:How to programmatically launch a Vuetify Dialog and wait for the response如何以编程方式启动 Vuetify 对话框并等待响应
【发布时间】:2019-09-25 07:55:30
【问题描述】:

我对 Vue.js 和 Vuetify 还很陌生(使用 AngularJS 好几年了,但我们公司正在改用 Vue.js)。我想要完成的是,当用户单击“登录”按钮时,它会进行一些检查(即用户名不能为空)并启动 Vuetify 对话框以提醒用户。我知道 Vuetify 有一些内置验证,但正在寻找更强大的东西,我可以等待响应(即当我需要等待类似的东西时,我可以使用你的历史记录/位置)。

基本上想做的事:

if (!userName){
    userName = await mbox('You must Enter your Username');
    return
}

或

var mProceed = await mbox('Can we use your location for awesome stuff?');

其中 mbox(一个简单的消息框弹出框)是一个函数,它返回一个 promise,以编程方式加载一个 vue 组件,将其添加到 dom,然后等待响应。

即

async function mbox (mText) {
    // load dialog component here and set message passed in
    // How Do I load the template / wait for it?
    return dialogResult

}

Vue 组件看起来像(按钮标题和文本是我传递给 mbox 函数的变量):

<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>

我可以编辑模板/添加 vue 组件的脚本我只是不确定如何通过返回承诺并等待响应的方法调用它?

【问题讨论】:

  • 我只注册了一次观察者并用 Promise await new Promise(resolve =&gt; { const stop = this.$watch('dialog', () =&gt; resolve(stop())) })

标签: javascript vue.js vuejs2 vuetify.js


【解决方案1】:

我的解决方案。

页面.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 "@/components/DlgWrapper";
import DlgFrame from "@/components/DlgFrame";

export default {
  data: () => {
    return {};
  },

  methods: {
    open_dlg: function(event) {
      this.$refs.dlg.open().then(result => {
        console.log("Result:", result);
      });
    }
  },

  components: {
    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>

祝你好运!

【讨论】:

    【解决方案2】:

    最终以这种方式解决它:

    我有一个名为 mbox(返回一个承诺)的方法,它创建我的组件的一个实例,将其添加到 DOM,然后监视该组件上的一个属性以了解用户选择了哪个选项。一旦用户选择了一个选项,就会兑现承诺

    我的 mbox 方法:

    import MBOX from './components/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 combining a bunch of stuff to make this work.
            // First, create the vue component
            var mboxInstance = _Vue.extend(MBOX); // mbox component, 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 component / element now that I'm done
                oEl.remove();
                resolve(Number(newVal));
            })
        }); // promise
    }, // mbox
    }
    

    还有我的 MBOX 组件:

    <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>
    

    然后我可以这样称呼它:

    var mChoice = await mbox('What do you want to do?', 'Title', 'Option 1', 'Option 2');
    

    或者对于简单的“验证”提示:

    if (!userName){
        mbox('You must enter a username');
        return;
    }
    

    【讨论】:

      【解决方案3】:
      //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;
          }
        }
      

      【讨论】:

        【解决方案4】:

        如果您要使用 Composition API 插件路线...我是使用 provide/inject + useDialog 可组合的。

        只需使用对话框提供程序组件包装您的模板组件:

        <template>
          <v-app>
            <v-main>
              <v-container>
                <DialogProvider>
                  <Nuxt />
                </DialogProvider>
              </v-container>
            </v-main>
          </v-app>
        </template>
        
        <script lang="ts">
        import { defineComponent } from '@nuxtjs/composition-api';
        import DialogProvider from '~/components/DialogProvider.vue';
        export default defineComponent({
          components: {
            DialogProvider,
          }
        });
        </script>
        

        然后,每当您想显示确认对话框时,只需像这样调用它:

        <script lang="ts">
        import { defineComponent } from '@nuxtjs/composition-api';
        import { useDialog } from '~/composables';
        export default defineComponent({
          setup() {
            const createConfirmDialog = useDialog();
            const handleDelete = async () => {
              try {
                const shouldProceed = await createConfirmDialog(
                  'Confirm',
                  'Delete this post?',
                  { width: 300 }
                );
                if (shouldProceed) {
                  // delete post
                }
              } catch (_e) {}
            };
            return {
              handleDelete
            };
          },
        });
        </script>
        

        完整代码在这里:https://gist.github.com/wobsoriano/1e63708d7771c34f835c0f6e3c5e731a

        【讨论】:

          猜你喜欢
          • 2021-02-25
          • 2017-11-10
          • 1970-01-01
          • 1970-01-01
          • 2013-02-12
          • 1970-01-01
          • 2015-04-27
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多