Dynamic component import

Hi there! Can anyone tell me why this code works:
<game-notepad-buildings-generic :building="buildingOnSelectedTile" />
<game-notepad-buildings-generic :building="buildingOnSelectedTile" />
But not this one:
<component is="game-notepad-buildings-generic"
:building="buildingOnSelectedTile"
/>
<component is="game-notepad-buildings-generic"
:building="buildingOnSelectedTile"
/>
According to the documentation, I should use resolveComponent but this code snippet doesn't work either:
<component :is="resolveComponent('game-notepad-buildings-generic')"
:building="buildingOnSelectedTile"
/>
<component :is="resolveComponent('game-notepad-buildings-generic')"
:building="buildingOnSelectedTile"
/>
And same is true for a camelCase formatted component name.
13 Replies
Unknown User
Unknown User14mo ago
Message Not Public
Sign In & Join Server To View
Romain 'Maz' B.
Romain 'Maz' B.OP14mo ago
Ok, I can't "pass the component" because the component depends on the api response. I moved these components inside the /global directory and it works perfectly 🙂 Thanks for your time The final code snippet:
<component :is="resolveComponentFor(buildingOnSelectedTile)" />
<component :is="resolveComponentFor(buildingOnSelectedTile)" />
function resolveComponentFor(building: Building) {
return resolveComponent(`game-notepad-buildings-${building.type}`)
}
function resolveComponentFor(building: Building) {
return resolveComponent(`game-notepad-buildings-${building.type}`)
}
Romain 'Maz' B.
Romain 'Maz' B.OP14mo ago
No description
Unknown User
Unknown User14mo ago
Message Not Public
Sign In & Join Server To View
Romain 'Maz' B.
Romain 'Maz' B.OP14mo ago
@orle I continued to investigate on the "why this fails":
resolveComponent(`game-notepad-buildings-generic`)
resolveComponent(`game-notepad-buildings-generic`)
And surprisingly, it was due to the backtick, you can read more on this here: https://github.com/nuxt/nuxt/discussions/17411#discussioncomment-2568712 Using quotes makes it work but still, I can't get it work with any "dynamic" string:
// Works
resolveComponent('game-notepad-buildings-generic')

// Doesn't work
const type = 'generic'
resolveComponent('game-notepad-buildings-'+type)
// Works
resolveComponent('game-notepad-buildings-generic')

// Doesn't work
const type = 'generic'
resolveComponent('game-notepad-buildings-'+type)
As of today, I have dozens of different building types, it's a Nuxt2 app that I'm converting to Nuxt3 and previously, it worked perfectly. So I would avoid a dozens of imports if possible as mentioned in your link
Unknown User
Unknown User14mo ago
Message Not Public
Sign In & Join Server To View
Romain 'Maz' B.
Romain 'Maz' B.OP14mo ago
Nope, the context is a browser game. I have a "notepad" displayed at left displaying presences on the selected tile, it display the building but also maybe some mobs, characters or resources so the "building" is not a page. Here is the template code of the TilePresence component which is using the buildings:
<template>
<div v-if="thingsOnTile" class="space-y-8 mx-4 mt-8">
<component :is="resolveComponentFor(buildingOnSelectedTile)"
v-if="buildingOnSelectedTile"
:building="buildingOnSelectedTile"
/>

<game-notepad-desert-tile v-if="currentCharacterIsOnSelectedTile && !buildingOnSelectedTile" />

<game-notepad-self-presence class="flex md:hidden lg:flex xl:hidden" />

<game-notepad-bag-presence v-if="bagsOnSelectedTile.length"
:bags="bagsOnSelectedTile"
/>

<game-notepad-other-presence v-for="character in charactersOnSelectedTile"
:key="character.id"
:character="character"
/>

<game-notepad-mob-presence v-for="mob in mobsOnSelectedTile"
:key="mob.id"
:mob="mob"
class="piece-of-paper"
/>
</div>
<div v-else class="text-center">
{{ I18n.t('there_is_nothing_there') }}
</div>
</template>
<template>
<div v-if="thingsOnTile" class="space-y-8 mx-4 mt-8">
<component :is="resolveComponentFor(buildingOnSelectedTile)"
v-if="buildingOnSelectedTile"
:building="buildingOnSelectedTile"
/>

<game-notepad-desert-tile v-if="currentCharacterIsOnSelectedTile && !buildingOnSelectedTile" />

<game-notepad-self-presence class="flex md:hidden lg:flex xl:hidden" />

<game-notepad-bag-presence v-if="bagsOnSelectedTile.length"
:bags="bagsOnSelectedTile"
/>

<game-notepad-other-presence v-for="character in charactersOnSelectedTile"
:key="character.id"
:character="character"
/>

<game-notepad-mob-presence v-for="mob in mobsOnSelectedTile"
:key="mob.id"
:mob="mob"
class="piece-of-paper"
/>
</div>
<div v-else class="text-center">
{{ I18n.t('there_is_nothing_there') }}
</div>
</template>
What are pages in this context are the "tabs" displayed on the notepad and changing the notepad view (game history, constructions, tile presence, ...) My temporary working solution is this one but it's ugly as possible 😬
// Import all building presence components
import {GameNotepadBuildingsArena,
GameNotepadBuildingsAttic,
GameNotepadBuildingsCapital,
GameNotepadBuildingsConstructionSite,
GameNotepadBuildingsFarm,
GameNotepadBuildingsField,
GameNotepadBuildingsGeneric,
// ...
} from '#components'

function resolveComponentFor(building: Building) {
// Mapping them into a key-value pair object
const map = {
'game-notepad-buildings-generic': GameNotepadBuildingsGeneric,
'game-notepad-buildings-arena': GameNotepadBuildingsArena,
'game-notepad-buildings-attic': GameNotepadBuildingsAttic,
'game-notepad-buildings-capital': GameNotepadBuildingsCapital,
'game-notepad-buildings-construction-site': GameNotepadBuildingsConstructionSite,
'game-notepad-buildings-farm': GameNotepadBuildingsFarm,
'game-notepad-buildings-field': GameNotepadBuildingsField,
// ...
}

// Return the final component
return map[`game-notepad-buildings-${building.type}`]
}
// Import all building presence components
import {GameNotepadBuildingsArena,
GameNotepadBuildingsAttic,
GameNotepadBuildingsCapital,
GameNotepadBuildingsConstructionSite,
GameNotepadBuildingsFarm,
GameNotepadBuildingsField,
GameNotepadBuildingsGeneric,
// ...
} from '#components'

function resolveComponentFor(building: Building) {
// Mapping them into a key-value pair object
const map = {
'game-notepad-buildings-generic': GameNotepadBuildingsGeneric,
'game-notepad-buildings-arena': GameNotepadBuildingsArena,
'game-notepad-buildings-attic': GameNotepadBuildingsAttic,
'game-notepad-buildings-capital': GameNotepadBuildingsCapital,
'game-notepad-buildings-construction-site': GameNotepadBuildingsConstructionSite,
'game-notepad-buildings-farm': GameNotepadBuildingsFarm,
'game-notepad-buildings-field': GameNotepadBuildingsField,
// ...
}

// Return the final component
return map[`game-notepad-buildings-${building.type}`]
}
No description
Romain 'Maz' B.
Romain 'Maz' B.OP14mo ago
To be shorter, I may use the "Solution 1" of this SO post to not import and then map Building components one by one https://stackoverflow.com/a/72711085/11896411 @orle I make it work with a pretty solution, I post it here for others
Unknown User
Unknown User14mo ago
Message Not Public
Sign In & Join Server To View
Romain 'Maz' B.
Romain 'Maz' B.OP14mo ago
import { defineAsyncComponent } from 'vue'
// Lazy imports from vite: https://vitejs.dev/guide/features.html#glob-import
const buildings = import.meta.glob('./notepad/buildings/**/*.vue')

const buildingComp = computed(() => {
const componentName = buildingOnSelectedTile.type

// Here using defineAsyncComponent: https://vuejs.org/guide/components/async.html#basic-usage
return defineAsyncComponent(buildings[`./notepad/buildings/${componentName}.vue`])
})
import { defineAsyncComponent } from 'vue'
// Lazy imports from vite: https://vitejs.dev/guide/features.html#glob-import
const buildings = import.meta.glob('./notepad/buildings/**/*.vue')

const buildingComp = computed(() => {
const componentName = buildingOnSelectedTile.type

// Here using defineAsyncComponent: https://vuejs.org/guide/components/async.html#basic-usage
return defineAsyncComponent(buildings[`./notepad/buildings/${componentName}.vue`])
})
<component :is="buildingComp"
v-if="buildingOnSelectedTile"
:building="buildingOnSelectedTile"
/>
<component :is="buildingComp"
v-if="buildingOnSelectedTile"
:building="buildingOnSelectedTile"
/>
This way, component are lazy imported and I don't need to maintain a mapping array for the new components 🔥
Unknown User
Unknown User14mo ago
Message Not Public
Sign In & Join Server To View
JonatasBraz
JonatasBraz5mo ago
is the same of this? const newComponent = defineAsyncComponent(() => import(~/components/path/to/component)); ? im currently using this, but the anything from the asyncComponent (props) is not beeing initializated
No description
JonatasBraz
JonatasBraz5mo ago
nvm, now is working XD. i just did a rollback and started again, probably mess something up on first time
Want results from more Discord servers?
Add your server