如何在自己以编程方式调用的组件中使用 Vuetify 3 组件,例如打开对话框?

huangapple go评论59阅读模式
英文:

How to use Vuetify 3 components in an own programmatically called component, e.g. open a dialog?

问题

我想使用this.$dialog.open('一些消息')来调用一个自定义对话框。

我的自定义对话框组件使用了一些 Vuetify 组件。

我一直遇到错误 Error: [Vuetify] Could not find defaults instance

有人有例子如何定义一个组件,以便它可以使用 Vuetify 吗?

这是我尝试的内容(缩短版):

主要文件(不包括导入等)

const app = createApp(App)
app.use(createVuetify())
app.use(dialog)

对话框文件

export default {
  install: (app) => {
    const instance = createInstance()
    app.config.globalProperties.$dialog = instance
    app.provide('$dialog', instance)
  },
}

function createInstance() {
  return {
    open(message) {
      const instance = createComponent(Dialog, { message }, document.body)
      return {
        dismiss: instance.ctx.dismiss
      }
    }
  }
}

function createComponent(component, props, parent, slots = {}) {
  const vNode = h(component, props, slots)
  render(vNode, parent)
  return vNode.component
}

dialog.vue 文件

<template>
  <v-dialog v-model="dialog" persistent width="auto">
    <v-card>
      {{ message }}
    </v-card>
  </v-dialog>
</template>

<script>
import { defineComponent } from 'vue'
export default defineComponent({
  name: 'Dialog',
  props: {
    message: { type: String, required: true },
  },
  data: () => ({ dialog: true }),
  methods: {
    dismiss() {},
  },
})
</script>
英文:

I want to call a custom dialog with this.$dialog.open(&#39;some message&#39;)

My own dialog component uses some vuetify components.

I always got the error Error: [Vuetify] Could not find defaults instance

Did somebody has an example how to define a component, that it could use vuetify?

This is what i try (shortened version):

main (without imports, etc.)

const app = createApp(App)
app.use(createVuetify())
app.use(dialog)

dialog

export default {
  install: (app) =&gt; {
    const instance = createInstance()
    app.config.globalProperties.$dialog = instance
    app.provide(&#39;$dialog&#39;, instance)
  },
}

function createInstance() {
  return {
    open(message) {
      const instance = createComponent(Dialog, { message }, document.body)
      return {
        dismiss: instance.ctx.dismiss
      }
    }
  }
}

function createComponent(component, props, parent, slots = {}) {
  const vNode = h(component, props, slots)
  render(vNode, parent)
  return vNode.component
}

dialog.vue

&lt;template&gt;
  &lt;v-dialog v-model=&quot;dialog&quot; persistent width=&quot;auto&quot;&gt;
    &lt;v-card&gt;
      {{ message }}
    &lt;/v-card&gt;
  &lt;/v-dialog&gt;
&lt;/template&gt;

&lt;script&gt;
import { defineComponent } from &#39;vue&#39;
export default defineComponent({
  name: &#39;Dialog&#39;,
  props: {
    message: { type: String, required: true },
  },
  data: () =&gt; ({ dialog: true }),
  methods: {
    dismiss() {},
  },
})
&lt;/script&gt;

答案1

得分: 1

你的代码几乎正确。只有在你的 createComponent() 函数中创建的 VNode 没有注册到当前应用程序,因此它无法访问到 Vuetify 组件(或来自应用程序的任何数据)。为了让它可以访问,你可以在 VNode 中注册应用程序,只需将应用程序传递给 createComponent(),然后设置 appContext 属性:

vNode.appContext = app._context

总之,这给你:

function createComponent(app, component, props, parent, slots = {}) {
  const vNode = h(component, props, slots)
  vNode.appContext = app._context
  render(vNode, parent)
  return vNode.component
}

以下是代码片段:

const { createApp, ref, defineComponent, h, render } = Vue;
const { createVuetify } = Vuetify
const vuetify = createVuetify()
const App = {
  inject:['$dialog'],
  methods: {
    showDialog(){
      console.log(this)
      this.$dialog.open('go go go')
    }
  }
}

const Dialog = {
  template: `
    <v-dialog v-model="dialog" persistent width="auto">
    <v-card>
      <v-card-text>{{ message }}</v-card-text>
    </v-card>
  </v-dialog>
  `,
  name: 'Dialog',
  props: {
    message: { type: String, required: true },
  },
  data: () => ({ dialog: true }),
  methods: {
    dismiss() {},
  },
}

const dialog = {
  install: (app) => {
    const instance = createInstance(app)
    app.config.globalProperties.$dialog = instance
    app.provide('$dialog', instance)
  },
}

function createInstance(app) {
  return {
    open(message) {
      const instance = createComponent(app, Dialog, { message }, document.body)
      return {
        dismiss: instance.ctx.dismiss
      }
    }
  }
}

function createComponent(app, component, props, parent, slots = {}) {
  const vNode = h(component, props, slots)
  vNode.appContext = app._context
  render(vNode, parent)
  return vNode.component
}

const app = createApp(App)
app.use(vuetify)
app.use(dialog)
app.mount('#app')

以下是HTML部分:

<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/vuetify@3/dist/vuetify.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-btn @click="showDialog">Show</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/dist/vuetify.min.js"></script>
英文:

You are very close. Only the VNode you create in your createComponent() function is not registered to the current app, so it does not have access to the Vuetify components (or any data from app). To give it access, you can register the app in the VNode, all you have to do is to pass the app to createComponent() and then set the appContext property:

vNode.appContext = app._context

all in all, this gives you:

function createComponent(app, component, props, parent, slots = {}) {
const vNode = h(component, props, slots)
vNode.appContext = app._context
render(vNode, parent)
return vNode.component
}

Here it is in a snippet:

<!-- begin snippet: js hide: true console: false babel: false -->

<!-- language: lang-js -->

const { createApp, ref, defineComponent, h, render } = Vue;
const { createVuetify } = Vuetify
const vuetify = createVuetify()
const App = {
inject:[&#39;$dialog&#39;],
methods: {
showDialog(){
console.log(this)
this.$dialog.open(&#39;go go go&#39;)
}
}
}
const Dialog = {
template: `
&lt;v-dialog v-model=&quot;dialog&quot; persistent width=&quot;auto&quot;&gt;
&lt;v-card&gt;
&lt;v-card-text&gt;{{ message }}&lt;/v-card-text&gt;
&lt;/v-card&gt;
&lt;/v-dialog&gt;
`,
name: &#39;Dialog&#39;,
props: {
message: { type: String, required: true },
},
data: () =&gt; ({ dialog: true }),
methods: {
dismiss() {},
},
}
const dialog = {
install: (app) =&gt; {
const instance = createInstance(app)
app.config.globalProperties.$dialog = instance
app.provide(&#39;$dialog&#39;, instance)
},
}
function createInstance(app) {
return {
open(message) {
const instance = createComponent(app, Dialog, { message }, document.body)
return {
dismiss: instance.ctx.dismiss
}
}
}
}
function createComponent(app, component, props, parent, slots = {}) {
const vNode = h(component, props, slots)
vNode.appContext = app._context
render(vNode, parent)
return vNode.component
}
const app = createApp(App)
app.use(vuetify)
app.use(dialog)
app.mount(&#39;#app&#39;)

<!-- language: lang-html -->

&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;https://cdn.jsdelivr.net/npm/vuetify@3/dist/vuetify.min.css&quot; /&gt;
&lt;link href=&quot;https://cdn.jsdelivr.net/npm/@mdi/font@5.x/css/materialdesignicons.min.css&quot; rel=&quot;stylesheet&quot;&gt;
&lt;div id=&quot;app&quot;&gt;
&lt;v-app&gt;
&lt;v-main&gt;
&lt;v-btn @click=&quot;showDialog&quot;&gt;Show&lt;/v-btn&gt;
&lt;/v-main&gt;
&lt;/v-app&gt;
&lt;/div&gt;
&lt;script src=&quot;https://unpkg.com/vue@3/dist/vue.global.prod.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/vuetify@3/dist/vuetify.min.js&quot;&gt;&lt;/script&gt;

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年6月5日 23:24:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/76407886.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定