programing

Vuetify 대화상자를 프로그래밍 방식으로 실행하고 응답을 기다리는 방법

goodcopy 2022. 8. 2. 23:46
반응형

Vuetify 대화상자를 프로그래밍 방식으로 실행하고 응답을 기다리는 방법

저는 Vue.js와 Vuetify에 대해 잘 모릅니다(Angular 사용).JS는 몇 년 전부터 Vue.js)로 전환하고 있습니다.사용자가 '사인인' 버튼을 클릭하면 몇 가지 확인(사용자 이름은 비워 둘 수 없음)을 수행하고 Vuetify Dialog를 실행하여 사용자에게 경고합니다.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(단순 메시지 상자 팝업 상자)는 약속을 반환하고 vue 컴포넌트를 프로그래밍 방식으로 로드하여 dom에 추가하고 응답을 기다리는 함수입니다.

Exmple 대화상자 스크린샷

예.

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 컴포넌트 스크립트 추가 중 약속을 반환하고 응답을 기다리는 메서드를 통해 템플릿을 호출하는 방법을 잘 모르겠습니다.

나의 해결책.

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 "@/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>

DLG Wrapper.표시하다

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

DLG 프레임표시하다

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

행운을 빕니다.

결국 다음과 같이 해결했습니다.

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
}

My 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;
}
//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;
    }
  }

만약 당신이 간다면Composition API플러그인 경로...를 사용하여 했습니다.provide/inject+ auseDialog컴포넌트 가능

템플릿 컴포넌트를 대화상자 공급자컴포넌트로 랩합니다.

<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 를 참조해 주세요.

언급URL : https://stackoverflow.com/questions/56026220/how-to-programmatically-launch-a-vuetify-dialog-and-wait-for-the-response

반응형