2

I'm trying to loop over a list of component described by strings (I get the name of the component from another , like const componentTreeName = ["CompA", "CompA"].

My code is a simple as:

<script setup>
    import CompA from './CompA.vue'
    import { ref } from 'vue'

    // I do NOT want to use [CompA, CompA] because my inputs are strings
    const componentTreeName = ["CompA", "CompA"]
</script>

<template>
  <h1>Demo</h1>
  <template v-for="compName in componentTreeName">
    <component :is="compName"></component>
  </template>
</template>

Demo here

EDIT

I tried this with not much success.

2
  • 2
    got a similar problem, hope it will help you stackoverflow.com/questions/66384268/… - in the answer is a working demo with a stringification Commented Feb 11, 2022 at 16:01
  • Thanks. The problem of the proposed solution is that the lookup function would not deal with globally registered components. I also tried to use resolveComponent, but I couldn't make it work. Commented Feb 11, 2022 at 16:14

4 Answers 4

5

When using script setup, you need to reference the component and not the name or key.

To get it to work, I would use an object where the string can be used as a key to target the component from an object like this:

<script setup>
    import CompA from './CompA.vue'
    import { ref } from 'vue'
    const components = {CompA};

    // I do NOT want to use [CompA, CompA] because my inputs are strings
    const componentTreeName = ["CompA", "CompA"]
</script>

<template>
  <h1>Demo</h1>
  <template v-for="compName in componentTreeName">
    <component :is="components[compName]"></component>
  </template>
</template>

To use a global component, you could assign components by pulling them from the app context. But this would require the app context to be available and the keys known.

example:

import { app } from '../MyApp.js'
const components = {
  CompA: app.component('CompA')
}

I haven't tested this, but this might be worth a try to check with getCurrentInstance

import { ref,getCurrentInstance } from 'vue'
const components = getCurrentInstance().appContext.components;
Sign up to request clarification or add additional context in comments.

10 Comments

Thanks, but unfortunately this solution does not work for components registered globally. And why is setup so special?
<script setup> is syntactical sugar that does a bunch of "magic" and in order to support that , some things just behave differently.
Ok. But does that mean that I can't support global components? I really don't want to assume that the parent component is aware of all components that can be defined in the child component.
updated answer, I haven't tested using the getCurrentInstance, but that might be worth a try too.
the problem in the sfc.vuejs examples is that the components are not getting registered as global components. In the example code and in the demos, you are using import CompA from './CompA.vue' which doesn't make it globally accessible, and leaves the getCurrentInstance().appContext.components object empty
|
5

Use resolveComponent() on the component name to look up the global component by name:

<script setup>
import { resolveComponent, markRaw } from 'vue'
const myGlobalComp = markRaw(resolveComponent('my-global-component'))
</script>

<template>
  <component :is="myGlobalComp" />
<template>

demo 1

If you have a mix of locally and globally registered components, you can use a lookup for local components, and fall back to resolveComponent() for globals:

<script setup>
import LocalComponentA from '@/components/LocalComponentA.vue'
import LocalComponentB from '@/components/LocalComponentB.vue'
import { resolveComponent, markRaw } from 'vue'

const localComponents = {
  LocalComponentA,
  LocalComponentB,
}

const lookupComponent = name => {
  const c = localComponents[name] ?? resolveComponent(name)
  return markRaw(c)
}

const componentList = [
  'GlobalComponentA',
  'GlobalComponentB',
  'LocalComponentA',
  'LocalComponentB',
].map(lookupComponent)
</script>

<template>
  <component :is="c" v-for="c in componentList" />
</template>

demo 2

Note: markRaw is used on the component definition because no reactivity is needed on it.

Comments

0

If you need your component repeated you can write this code:

<script setup>
import CompA from './CompA.vue'
import { computed, ref } from 'vue'

// Example input
const componentTreeName = ["CompA", "CompA"]

// Map component names to actual components
const components = {
  CompA,
}

const componentsToShow = computed(() => 
  componentTreeName.map(name => components[name])
)
</script>

<template>
  <h1>Demo</h1>
  <template v-for="(comp, index) in componentsToShow" :key="index">
    <component :is="comp"></component>
  </template>
</template>

Comments

0

I modified your code to this and this works.

<script setup>
    import CompA from './CompA.vue'
    import { ref, getCurrentInstance } from 'vue'

  const components = getCurrentInstance().appContext.components;
  // I do NOT want to use [CompA, CompA] because my inputs are strings
    const componentTreeName = [CompA, CompA]
</script>

<template>
  <h1>Demo</h1>
  <template v-for="compName in componentTreeName">
    <component :is="compName"></component>
  </template>
</template>

Refer to this link to see working example -> https://play.vuejs.org/#eNqVU8Fu2zAM/RVOl6RAZmPdTpkboEsLLDukxRZgh3kH1WYSZbZkSHSaIfD/7D/2Y6PkJHWbbGsvhsX3SD4+SltxWVXRukYxFInLrKoIHFJdjVKdkiorYwnGpqwuYW5NCb0oDief0utQtmBxPoAF0ri2FjVNtCOpM4Rml7hL0ACZYYy/nKmZ6eDiRF7/LJJVNTaacEPRA/m9rxDHMIHcwPRmBvdSE5CB2iF8C9oGreDvcIeZ9OHyJyhd1dxJWgRHVumF8+KfKJlZxKkskQU9rpTqJG7N8bbohLCsCknIJ4Bk+WZ0haVJYv4JgT0M69dzYy9S4TuEwkofd0tFyOK8AwRD5TppzEjiA9j2iDsaOgcxEIf9nFhpd1tHm2ndKN2C52e837ut2VlFBXtWqBxhiRZ7Z/8x43y03YYiTcOOnLfRYD/bUZocC56M8TD2Y+UzeVfgy5UrvcKM+lxVKn0lSaaCRR5N8xGLwsBXY4v81TOmaHcSJEGODkpJ6vcvi3xxvP07xtMRyHHfuVpEK2c0j7ENpLBJVaC9qUixrlQMISAekyzr/lOIka1xsI9nS8x+nIiv3MbHUnHLYtCu+XocMJKWn1ILX3+Z8tPpgGx+XTD7H+BndKaovcaW9qHWOcvu8ILaSVgGv6KZu94Qarcfygv1zCbwU8EL8tfxb6M/yH0bvQt5qW5E8wfK75B9

1 Comment

Thanks, but for what I see, you precisely wrote what I don't want to write (cf comment), i.e. you don't use a string but the variable of the component directly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.