-1

I want to implement common modal function using 'v-model'. My codes are like this below: Actually, the function does work(it shows alert messages) but dialogs which have 'v-model' pop up continuosly even they are 'false'. Please help me..T_T

<template>
      <v-dialog  v-model="modals.add_directory">
         test dialog
      </v-dialog>
</template>

<script lang="ts" setup>

const modals = {
          add_directory: ref(false),
          add_lvm: ref(false),
          add_lvmthin: ref(false),
          add_nfs: ref(false),
          add_smbcifs: ref(false),
          add_cephfs: ref(false),
          add_rbd: ref(false),
          add_zfs: ref(false),
};

const open_modal = (modalName) => {
    alert(modals[modalName].value);
    modals[modalName].value = !modals[modalName].value;
};

</script>
Moritz Ringler
  • 9,772
  • 9
  • 21
  • 34
  • Instead of using `const modals = { foo: ref(false), bar: ref(false) }` you can use `const modals = reactive({ foo: false, bar: false })`. The advantage is you no longer need to use `.value` in script. `modals.foo` is reactive and doesn't need `.value`. Also, the problem with your code is that `modals` object itself is not reactive, so Vue has no way of knowing when you change one of its properties, even if that property is reactive. – tao Apr 03 '23 at 11:01

1 Answers1

0

Yes, this is a somewhat irritating behavior of Vue, where refs in the template are not unwrapped when they come from an array or object (see this bug for reference). You will have to unwrap it yourself, i.e. write modals.add_directory.value:

<v-dialog  v-model="modals.add_directory.value">
  ...
</v-dialog>

You can see it working like that in the snippet:

const { createApp, ref } = Vue;
const { createVuetify } = Vuetify
const vuetify = createVuetify()
const app = {
  setup(){
    const modals = {
      addDirectory: ref(false),
    };
    
    const openModal = (modalName) => {
      modals[modalName].value = !modals[modalName].value;
    };

    return { openModal, modals }
  }

}
createApp(app).use(vuetify).mount('#app')
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/vuetify@3.1.8/dist/vuetify.min.css" />
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/vuetify@3.1.8/dist/vuetify-labs.min.css" />
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@5.x/css/materialdesignicons.min.css" rel="stylesheet">
<div id="app">
  <v-app>
    <v-main>
    
      <v-dialog v-model="modals.addDirectory.value">
        <v-card>
          <v-card-title>
            The add directory dialog 
          </v-card-title>
        </v-card>
      </v-dialog>

      <v-btn @click="openModal('addDirectory')" class="ma-4">
        add directory
      </v-btn>

    </v-main>
  </v-app>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@3.1.8/dist/vuetify.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@3.1.8/dist/vuetify-labs.js"></script>

Alternatively, you could use reactive, which does not have this limitation:

    const modals = reactive({
      addDirectory: false,
    });
    
    const openModal = (modalName) => {
      modals[modalName] = !modals[modalName];
    };

const { createApp, reactive } = Vue;
const { createVuetify } = Vuetify
const vuetify = createVuetify()
const app = {
  setup(){
    const modals = reactive({
      addDirectory: false,
    });
    
    const openModal = (modalName) => {
      modals[modalName] = !modals[modalName];
    };

    return { openModal, modals }
  }

}
createApp(app).use(vuetify).mount('#app')
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/vuetify@3.1.8/dist/vuetify.min.css" />
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/vuetify@3.1.8/dist/vuetify-labs.min.css" />
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@5.x/css/materialdesignicons.min.css" rel="stylesheet">
<div id="app">
  <v-app>
    <v-main>
    
      <v-dialog v-model="modals.addDirectory">
        <v-card>
          <v-card-title>
            The add directory dialog 
          </v-card-title>
        </v-card>
      </v-dialog>

      <v-btn @click="openModal('addDirectory')" class="ma-4">
        add directory
      </v-btn>

    </v-main>
  </v-app>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@3.1.8/dist/vuetify.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@3.1.8/dist/vuetify-labs.js"></script>

Does that work for you?

Moritz Ringler
  • 9,772
  • 9
  • 21
  • 34