Open a Vuetify dialog from a component template in VueJS

JavascriptVuejs2Vue Componentvuetify.js

Javascript Problem Overview


I'm using the VueJS Vuetify framework and I need to open a dialog - that gets imported as a component template - from another template. Once the Menu button in App.vue got clicked, the Modal should open. Here is my setup:

  • App.vue = navigation template with Menu button
  • Modal.vue = Modal template, imported as global in main.js

main.js

import Modal from './components/Modal.vue'
Vue.component('modal', Modal)

Modal.vue Template:

<template>
  <v-layout row justify-center>
    <v-btn color="primary" dark @click.native.stop="dialog = true">Open Dialog</v-btn>
    <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.native="dialog = false">Disagree</v-btn>
          <v-btn color="green darken-1" flat="flat" @click.native="dialog = false">Agree</v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>
  </v-layout>
</template>
<script>
  export default {
    data () {
      return {
        dialog: false
      }
    }
  }
</script>

How to open the dialog?

Javascript Solutions


Solution 1 - Javascript

No event bus needed and v-model

Update:

When I first answered this, I posted my answer as a "workaround", since it didn't felt completely "right" at the time and I was new to Vue.js. I wanted to open or close the dialog by using a v-model directive, but I couldn't get there. After some time I found how to do this in the docs, using the input event and the value property, and here's how I think it should be done without an event bus.

Parent component:

<template>
   <v-btn color="accent" large @click.stop="showScheduleForm=true">    
   <ScheduleForm v-model="showScheduleForm" />
</template>

<script>
import ScheduleForm from '~/components/ScheduleForm'

export default {
  data () {
    return {
      showScheduleForm: false
    }
  },
  components: {
    ScheduleForm
  }
}
</script>

Child component (ScheduleForm):

<template>
<v-dialog v-model="show" max-width="500px">
  <v-card>
    <v-card-actions>
      <v-btn color="primary" flat @click.stop="show=false">Close</v-btn>
    </v-card-actions>
  </v-card>
</v-dialog>
</template>

<script>
export default {
  props: {
     value: Boolean
  },
  computed: {
    show: {
      get () {
        return this.value
      },
      set (value) {
         this.$emit('input', value)
      }
    }
  }
}
</script>
Original answer:

I was able to work around this without the need for a global event bus.

I used a computed property with a getter AND a setter. Since Vue warns you about mutating the parent property directly, in the setter I simply emitted an event to the parent.

Here's the code:

Parent component:

<template>
   <v-btn color="accent" large @click.stop="showScheduleForm=true"></v-btn>   
   <ScheduleForm :visible="showScheduleForm" @close="showScheduleForm=false" />
</template>

<script>
import ScheduleForm from '~/components/ScheduleForm'

export default {
  data () {
    return {
      showScheduleForm: false
    }
  },
  components: {
    ScheduleForm
  }
}
</script>

Child component (ScheduleForm):

<template>
<v-dialog v-model="show" max-width="500px">
  <v-card>
    <v-card-actions>
      <v-btn color="primary" flat @click.stop="show=false">Close</v-btn>
    </v-card-actions>
  </v-card>
</v-dialog>
</template>

<script>
export default {
  props: ['visible'],
  computed: {
    show: {
      get () {
        return this.visible
      },
      set (value) {
        if (!value) {
          this.$emit('close')
        }
      }
    }
  }
}
</script>

Solution 2 - Javascript

> There are many ways to do it such is Vuex,Event Bus,Props with which you can manage whether the modal have to open or to close.I will show you my favourite way using the .sync modifier:

First i will simplify you question(the code part)

Parent component

<template>
   <div>
     <button @click="dialog=true">Open Dialog</button>
     <Child :dialog.sync="dialog" />
   </div>
</template>

<script>
import Child from './Child.vue'
export default {
    components: {
  	  Child
    },
	data: {
  	  return {
    	dialog: false
      }
   }
}
</script>

Child(Dialog) Component

<template>
  <v-layout row justify-center>
    <v-dialog v-model="dialog" persistent 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 @click.native="close">Close</v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>
  </v-layout>
</template>

<script>
	
  export default {
  	props: {
    	dialog: {
      	default: false
      }
    },
    methods: {
    	close() {
      	this.$emit('update:dialog', false)
      }
    }
  }

</script>

Solution 3 - Javascript

Simple minimal working example

codepen

Pass value prop as value to v-dialog component, and from child dialog emit input event whenever you want to close it:

//CustomDialog.vue
<v-dialog :value="value" @input="$emit('input', $event)">
  <v-btn color="red" @click.native="$emit('input', false)">Close</v-btn>
</v-dialog>
...
props:['value']

and add v-model to your parent

//Parent.vue
<custom-dialog v-model="dialog">

So no custom event bus, no data, no watch, no computed.

Solution 4 - Javascript

You can open the dialog using custom events and using an event bus for non parent-child communication.

If your application gets a bit more complex I recommend you use Vuex for state management.


Event bus solution:

In your main.js or in a new file create and export a new Vue instance :

export const bus = new Vue()

In app.vue import the busand emit the event:

<template>
  <div>
    <button @click.prevent="openMyDialog()">my button</button>
  </div>
</template>

<script>
  import {bus} from '../main' // import the bus from main.js or new file
  export default {
    methods: {
      openMyDialog () {
        bus.$emit('dialog', true) // emit the event to the bus
      }
    }
  }
</script>

In modal.vue also import the bus and listen for the event in the created hook:

<script>
  import {bus} from '../main'    
  export default {
    created () {
      var vm = this
      bus.$on('dialog', function (value) {
        vm.dialog = value
      })
    }
  }
</script>

Solution 5 - Javascript

The most simpler way I found to do it is:

in data() of component, return a attribute, let's say, dialog.

When you include a component, you can set a reference to your component tag. E.g.:

import Edit from '../payment/edit.vue';

<edit ref="edit_reference"></edit>

Then, inside my component, I have set a method:

        open: function () {
            var vm = this;

            vm.dialog = true;
        }

Finally, I can call it from parent, using:

  editar(item)
  {
      var vm = this;

      vm.$refs.edit_reference.open();
  }

Solution 6 - Javascript

I prefer use this:

DialogConfirm.vue

<template>
  <v-dialog :value="visible" max-width="450">
    <v-card>
      <v-card-title v-text="title" />
      <v-card-text v-text="message" />
      <v-card-actions v-if="visible">
        <template v-for="action in value">
          <v-spacer :key="action.label" v-if="typeof action == 'string'" />
          <v-btn
            v-else
            text
            :key="action.label"
            v-text="action.label"
            @click="doAction(action.action)"
            :color="action.color"
          />
        </template>
      </v-card-actions>
    </v-card>
  </v-dialog>
</template>
<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component';
import { Prop, Watch } from 'vue-property-decorator';

@Component
export default class DialogConfirm extends Vue {

  @Prop({ type: String, default: "Confirm" })
  title: string

  @Prop({ type: String, default: "Are you sure?" })
  message: string

  @Prop({ type: Array, default: undefined })
  value: { label: string, action: () => boolean, color: string }[]

  get visible() {
    return Array.isArray(this.value) && this.value.length > 0
  }

  doAction(action: () => boolean) {
    if ('undefined' == typeof action || action() !== false) {
      this.$emit('input', null)
    }
  }
}
</script>

Usage Example

/** Disable AP Mode */
  setApMode(enable: boolean) {
    const action = () => {
      Api.get('wifi', {
        params: {
          ap: enable
        }
      }).then(response => this.$store.dispatch('status'))
    }
    if (enable == true) {
      // No confirmation
      return action();
    }
    this.dialogTitle = 'Are you sure?'
    this.dialogMessage = "you may lost connection to this device.";
    this.dialogActions = [
      {
        label: 'Cancel',
        color: 'success'
      },
      'spacer',
      {
        label: "OK, Disable it",
        color: "error",
        action
      }
    ]
  }

Solution 7 - Javascript

within your App.vue template add this

<modal></model>

it will render your current Modal.vue template with v-btn and v-dialog

now inside it there will be one button - Open Dialog when you click on that modal will open.

Solution 8 - Javascript

methods: {
  openDialog(e) {
    this.dialog = true;
  }
},

This one works for me

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTomView Question on Stackoverflow
Solution 1 - JavascriptMatheus Dal'PizzolView Answer on Stackoverflow
Solution 2 - JavascriptRolandView Answer on Stackoverflow
Solution 3 - JavascriptTraxoView Answer on Stackoverflow
Solution 4 - JavascriptSolenoView Answer on Stackoverflow
Solution 5 - JavascriptMarcoView Answer on Stackoverflow
Solution 6 - JavascriptMochamad ArifinView Answer on Stackoverflow
Solution 7 - JavascriptHardik SatasiyaView Answer on Stackoverflow
Solution 8 - JavascriptPiatosView Answer on Stackoverflow