Search code examples
vue.jsvuetify.js

How to open a v-dialog from parent component


I'm currently working on a frontend with Vuetify 3 and I was thinking about the DRY principle several times now. But sometimes I'm having issues wrapping my head around Vue and Vuetify and therefore give up on DRY.

My current example is using a reusable v-dialog for selecting an item out of a list. I want a button in the parent component to activate the child component, which is the dialog. If I select an item in the dialog, it will emit this item.

The problem I'm facing now is that I can't get the dialog to open in the first place.

Here is the child component CustomSelector:

<template>
    <v-dialog>
        Some logic to displace the list and emit the selection
    </v-dialog>
</template>

In the parent component I have a button, that toggles the v-model for the child.

<template>
    <div>
        <v-btn @click="toggleCustomSelection">Select</v-btn>

        <custom-selector v-model="showCustomSelector"></custom-selector>
    </div>
</template>

<script setup>
    // imports and stuff

    const showCustomSelector = ref(false);

    function toggleCustomSelection() {
        showCustomSelector.value = !showCustomSelector.value;
    }
</script>

If I don't implement the CustomSelector as it's own component but directly put the actual dialog in the parent component, then it works. But as soon as I outsource it to it's own component, it doesn't work anymore.

How can I make this work? If this is not the usual way to do it, what is a better way?


Solution

  • I found a way to achieve what I am looking for. This question has lead me into the right direction. (EDIT: I found an even better way)

    For anyone trying to achieve the same: You have to define the modelValue prop in the child component , create a computed value and assign in to the v-dialog as v-model and v-bind it to the modelValue of the v-dialog. Here is the result.

    Parent component

    <template>
        <div>
            <v-btn @click="toggleCustomSelection">Select</v-btn>
    
            <custom-selector v-model="showCustomSelector"></custom-selector>
        </div>
    </template>
    
    <script setup>
        import { ref } from 'vue'
    
        const showCustomSelector = ref(false);
    
        function toggleCustomSelection() {
            showCustomSelector.value = !showCustomSelector.value;
        }
    </script>
    

    Child component

    <template>
        <v-dialog :model-value="modelValue">
            Some logic to displace the list and emit the selection
    
            <v-btn @click="close">Close</v-btn>
        </v-dialog>
    </template>
    
    <script setup>    
        const props = defineProps({ modelValue: Boolean });
        const emit = defineEmits(['update:modelValue']);
    
        function close() { emit('update:modelValue', false); }
    </script>
    

    Now if you want the child component to actually pass data to the parent, you can simply add additional emits to the child and listen to them in the parent.