简体   繁体   English

从 VueJS 中的组件模板打开 Vuetify 对话框

[英]Open a Vuetify dialog from a component template in VueJS

I'm using the VueJS Vuetify framework and I need to open a dialog - that gets imported as a component template - from another template.我正在使用 VueJS Vuetify 框架,我需要从另一个模板打开一个对话框 - 作为组件模板导入。 Once the Menu button in App.vue got clicked, the Modal should open.一旦App.vue中的Menu按钮被点击,Modal 应该会打开。 Here is my setup:这是我的设置:

  • App.vue = navigation template with Menu button App.vue = 带有菜单按钮的导航模板
  • Modal.vue = Modal template, imported as global in main.js Modal.vue = 模态模板,在 main.js 中作为全局导入

main.js main.js

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

Modal.vue Template: Modal.vue 模板:

<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?如何打开对话框?

No event bus needed and v-model不需要事件总线和 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.当我第一次回答这个问题时,我将我的答案发布为“解决方法”,因为当时感觉它并不完全“正确”,而且我是 Vue.js 的新手。 I wanted to open or close the dialog by using a v-model directive, but I couldn't get there.我想使用v-model指令打开或关闭对话框,但我无法到达那里。 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.一段时间后,我在文档中找到了如何使用input 事件value 属性来执行此操作,这就是我认为在没有事件总线的情况下应该如何完成的。

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):子组件(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.我使用了一个带有 getter 和 setter 的计算属性。 Since Vue warns you about mutating the parent property directly, in the setter I simply emitted an event to the parent.由于 Vue 会警告您直接更改父属性,因此在设置器中我只是向父级发出了一个事件。

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):子组件(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>

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:有很多方法可以做到这一点,例如 Vuex、Event Bus、Props,您可以使用它们来管理模式是否必须打开或关闭。我将向您展示我最喜欢的使用.sync修饰符的方式:

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>

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:value prop 作为value传递给v-dialog组件,并在您想要关闭它时从子对话框发出input事件:

//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并将 v-model 添加到您的父级

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

So no custom event bus, no data , no watch , no computed .所以没有自定义事件总线,没有data ,没有watch ,没有computed

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 .如果您的应用程序变得有点复杂,我建议您使用Vuex 进行状态管理


Event bus solution:事件总线解决方案:

In your main.js or in a new file create and export a new Vue instance :在您的main.js或新文件中创建并导出一个新的 Vue 实例:

export const bus = new Vue()

In app.vue import the bus and emit the event:app.vue 中导入bus并发出事件:

<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:modal.vue 中还导入总线并在 created 钩子中监听事件:

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

The most simpler way I found to do it is:我发现最简单的方法是:

in data() of component, return a attribute, let's say, dialog.在组件的 data() 中,返回一个属性,比如说对话框。

When you include a component, you can set a reference to your component tag.包含组件时,可以设置对组件标记的引用。 Eg:例如:

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();
  }

I prefer use this:我更喜欢使用这个:

DialogConfirm.vue 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
      }
    ]
  }

within your App.vue template add this在你的App.vue template中添加这个

<modal></model>

it will render your current Modal.vue template with v-btn and v-dialog它将使用v-btnv-dialog呈现您当前的Modal.vue模板

now inside it there will be one button - Open Dialog when you click on that modal will open.现在里面会有一个button - 当您单击该模式时Open Dialog将打开。

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

This one works for me这个对我有用

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM