tobiasBora
tobiasBora

Reputation: 2372

Vuejs 3 : components scope, and how to provide components to slots

I'd like in Vue 3 to be able to provide/overwrite existing components to slots by doing something like:

  <ProvideFun>
    <p>
      Hello
      <FunA></FunA>
    </p>
  </ProvideFun>
  <ProvideOtherFun>
    <p>
      Hello
      <FunA></FunA>
    </p>
  </ProvideOtherFun>

in order to be allowed to use some components (here <FunA>) inside others components (here <ProvideFun> and <ProvideOtherFun>). Note that ProvideFun and ProvideOtherFun may provide different versions of the <FunA> component.

How could I do something like that?

You can find a demo here.

Upvotes: 0

Views: 1159

Answers (1)

tony19
tony19

Reputation: 138226

Option 1: Provide/inject components

The parent (i.e., ProvideFun or ProvideOtherFun) could provide its own component definition (named "comp") to be used in FunA:

<!-- ProvideFun.vue -->
<script setup>
import { provide } from 'vue'
import CompA from './CompA.vue'
          👇
provide('comp', CompA)
</script>

<template>
  <h1>
    Provide fun
  </h1>
  <slot />
</template>
<!-- ProvideOtherFun.vue -->
<script setup>
import { provide } from 'vue'
import CompB from './CompB.vue'
          👇
provide('comp', CompB)
</script>

<template>
  <h1>
    Provide other fun
  </h1>
  <slot />
</template>

Make FunA.vue a <component> wrapper that injects the "comp" component definition from a parent:

<!-- FunA.vue -->
<script setup>
import { inject } from 'vue'
       👇
const comp = inject('comp')
</script>

<template>         👇
  <component :is="comp" />
</template>

This requires registering the FunA placeholder component before it could be used in ProvideFun/ProvideOtherFun:

<script setup>
import ProvideFun from './ProvideFun.vue'
import ProvideOtherFun from './ProvideOtherFun.vue'
        👇
import FunA from './FunA.vue'
</script>

<template>
  <ProvideFun>
    <p>
      Hello
      <FunA />
    </p>
  </ProvideFun>
  
  <ProvideOtherFun>
    <p>
      Hello
      <FunA />
    </p>
  </ProvideOtherFun>
</template>

demo 1

Option 2: Components via slot props

Bind the CompA component definition as a slot prop (named "FunA") in ProvideFun.vue, and CompB in ProvideOtherFun.vue:

<!-- ProvideFun.vue -->
<script setup>
import CompA from './CompA.vue'
</script>

<template> 👇
  <slot :FunA="CompA">
</template>
<!-- ProvideOtherFun.vue -->
<script setup>
import CompB from './CompB.vue'
</script>

<template> 👇
  <slot :FunA="CompB">
</template>

Then use a <component> to render it in the parent:

<script setup>
import ProvideFun from './ProvideFun.vue'
import ProvideOtherFun from './ProvideOtherFun.vue'
</script>

<template>
  <ProvideFun>           👇
    <template v-slot="{ FunA }">
      <p>
        Hello            👇
        <component :is="FunA" />
      </p>
    </template>
  </ProvideFun>
  
  <ProvideOtherFun>      👇
    <template v-slot="{ FunA }">
      <p>
        Hello            👇
        <component :is="FunA" />
      </p>
    </template>
  </ProvideOtherFun>
</template>

demo 2

Upvotes: 2

Related Questions