search

Home  >  Q&A  >  body text

Is there a way to declare provision/injection in a Vue3 dynamic component outside of the host settings function?

<p>I'm creating a <code>Dynamic Component</code> in <code>Vue3</code>. I use <code>v-bind</code> to provide <code>props</code>. </p> <pre class="lang-js prettyprint-override"><code><component :is='MyComponent' v-bind='myProps' /> </code></pre> <p>I want to provide/inject</code> functionality using <code>. How can I put the properties I provide into the dynamic component. My dynamic component calls <code>inject</code> in the <code>setup</code> function and expects a </code> value for its child component <code>. </p> <p>While this is not documented on Vue, I tried it without success: </p> <pre class="brush:php;toolbar:false;"><component :is='MyComponent' v-bind='myProps' :provide='myProvidedProps'/></pre> <p>Even tried putting the <code>provide</code> object into the <code>props</code> object. </p>
P粉384366923P粉384366923452 days ago499

reply all(1)I'll reply

  • P粉122932466

    P粉1229324662023-08-31 00:50:39

    After browsing the Vue3 source code, there is no way to providespecifications directly in the template to dynamic components. It must be called in the settings function or options of the parent hosting the dynamic component, or in the settings or options of the dynamic component.

    The two options are:

    1. You call provide on the component that hosts the dynamic component.
    setup() {
      provide('message', 'hello')
    }
    <template>
      <component :is='myComponent' />
    </template>

    This doesn't work for me because my setter function is called before my dynamic component is activated; I also need the component type to be set along with the provided value.

    1. Send the items to be provided as props to the component and let the dynamic component call them.
    function setComponent(someImportedComponent, providedValues) {
      myComponent.value = someImportedComponent
      myProps.value = {
        toProvide: providedValues
      }
    }
    <template>
      <component :is='myComponent' v-bind='myProps' />
    </template>

    My component

    setup() {
      for(let [key,value] of Object.entries(props.toProvide) ) {
        provide(key, value)
      }
    }

    Now this has its problems, as each dynamic component now needs to be responsible for understanding and calling the incoming provider.

    Solution 1

    The solution to each component needing to know the value provided is to create an intermediate component that provides the value.

    Available (intermediate components)

    <script setup lang="ts">
    import {provide} from 'vue'
    
    const props = defineProps<{
      is: any
      provide?: Record<string, any>
      [key: string]: any
    }>()
    
    if (props.provide) {
      for (const [key, value] of Object.entries(props.provide)) {
        provide(key, value)
      }
    }
    
    const _props = Object.fromEntries(Object.entries(props).filter(it => {
      return it[0] !== 'is' && it[0] !== 'provide'
    }))
    </script>
    
    <template>
      <component :is="is" v-bind="_props"/>
    </template>

    Use it like this:

    <template>
      <providable :is="myComponent" :provide='toProvide' v-bind='myProps' />
    </template>

    Solution 2

    A cleaner solution is to create a wrapper component, similar to how keep-alive works. The target component only needs to be put into the default slot.

    Provide.vue

    <script setup lang="ts">
    import {provide} from 'vue'
    
    const props = defineProps<{
      value: Record<string, any>
    }>()
    
    for (const [key, value] of Object.entries(props.value)) {
      provide(key, value)
    }
    </script>
    
    <template>
      <slot name="default"/>
    </template>

    and use it like this:

    <template>
      <provide value='toProvide'>
        <my-component v-bind='myProps' />
      </provide>
    </template>

    reply
    0
  • Cancelreply