1

The title is basically my question. But the following code should make clearer what my goal is.

I have a version of this:

// AppItem.vue
<script>
import { h } from 'vue'

import { AppItem1 } from './item-1';
import { AppItem2 } from './item-2';
import { AppItem3 } from './item-3';

const components = {
    AppItem1,
    AppItem2,
    AppItem3,
};

export default {
    props: {
        level: Number
    },
    render() {
        return h(
            components[`AppItem${this.level}`],
            {
                ...this.$attrs,
                ...this.$props,
                class: this.$attrs.class + ` AppItem--${this.level}`,
            },
            this.$slots
        )
    }
}
</script>

// AppItem1.vue
<template>
    <AppBlock class="AppItem--1">
        <slot name="header">
            #1 - <slot name="header"></slot>
        </slot>
        <slot></slot>
    </AppBlock>
</template>

// AppItem2.vue
<template>
    <AppBlock class="AppItem--2">
        <template #header>
            #2 - <slot name="header"></slot>
        </template>

        <slot></slot>
    </AppBlock>
</template>

// AppBlock.vue
<template>
    <div class="AppBlock">
        <div class="AppBlock__header">
            <slot name="header"></slot>
        </div>
        <div class="AppBlock__body">
            <slot></slot>
        </div>
    </div>
</template>

And my goal would be to use <AppItem> like...

<AppItem level="1">
    <template #header>
        Animal
    </template>

    <AppItem level="2">
        <template #header>
            Gorilla
        </template>
        <p>The gorilla is an animal...</p>
    </AppItem>

    <AppItem level="2">
        <template #header>
            Chimpanzee
        </template>
        <p>The Chimpanzee is an animal...</p>
    </AppItem>
</AppItem>

...and have it render like...

<div class="AppBlock AppItem AppItem--1">
    <div class="AppBlock__header">
        Animal
    </div>

    <div class="AppBlock__body">

        <div class="AppBlock AppItem AppItem--2">
            <div class="AppBlock__header">
                Gorilla
            </div>

            <div class="AppBlock__body">
                <p>The gorilla is an animal...</p>
            </div>
        </div>

        <div class="AppBlock AppItem AppItem--2">
            <div class="AppBlock__header">
                Chimpanzee
            </div>

            <div class="AppBlock__body">
                <p>The Chimpanzee is an animal...</p>
            </div>
        </div>
    </div>
</div>

Why does it not work? What I'm I misunderstaning?

João Paulo Macedo
  • 15,297
  • 4
  • 31
  • 41

1 Answers1

0

The right way to get the AppItem--N component by name is to use the resolveComponent() function:

const appItem = resolveComponent(`AppItem${props.level}`);

I also fixed a couple of other small problems and had to rewrite the <setup script> to the setup() function. Now it works.

Playground

const { createApp, h, resolveComponent  } = Vue;

const AppBlock = { template: '#appblock' }
const AppItem1 = { components: { AppBlock }, template: '#appitem1' }
const AppItem2 = { components: { AppBlock }, template: '#appitem2' }

const AppItem = {
  components: {
    AppItem1, AppItem2, AppBlock
  },
  props: {
    level: Number
  },
  setup(props, { attrs, slots, emit, expose } ) {
      const appItem = resolveComponent(`AppItem${props.level}`);
      return () => 
            h(appItem, {
                ...attrs,
                ...props,
                class: (attrs.class ? attrs.class : '') + " AppItem--" + props.level,
            }, slots);
   }
}

const App = { components: { AppItem } }
const app = createApp(App)
app.mount('#app')
#app { line-height: 1; }
[v-cloak] { display: none; }
<div id="app">  
  <app-item :level="1">
      <template #header>
          <h4>Animal</h4>
      </template>
      <app-item :level="2">
          <template #header>
              Gorilla
          </template>
          <p>The gorilla is an animal...</p>
      </app-item>
      <app-item :level="2">
          <template #header>
              Chimpanzee
          </template>
          <p>The Chimpanzee is an animal...</p>
      </app-item>
  </app-item>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script type="text/x-template" id="appitem1">
    <app-block class="AppItem">
        <slot name="header">
            #1 - <slot name="header"></slot>
        </slot>
        <slot></slot>
    </app-block>
</script>
<script type="text/x-template" id="appitem2">
    <app-block class="AppItem">
        <template #header>
            #2 - <slot name="header"></slot>
        </template>
        <slot></slot>
    </app-block>
</script>
<script type="text/x-template" id="appblock">
    <div class="AppBlock">
        <div class="AppBlock__header">
            <slot name="header"></slot>
        </div>
        <div class="AppBlock__body">
            <slot></slot>
        </div>
    </div>
</script>
Tolbxela
  • 4,767
  • 3
  • 21
  • 42