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 butto
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>
methods: {
openDialog(e) {
this.dialog = true;
}
},
This one works for me
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
}
]
}
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 a 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>
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>
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
.
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.