Dynamically loading components in Vue - vue.js

I have a component that is dynamically loading based on the value of the variable "scene". The problem is I have to import and load all the possible scenes under the components, and I have a lot of different possible components. Is it possible for Vue to just dynamically import the passed in scene, or do I need to import everything at the start?
For other reasons, I would prefer not to use a router for this case.
<component
:is="scene"
v-bind="options"
>
</component>
=========
import TitleSceneComponent
from './scenes/common/TitleSceneComponent.vue';
import NarrationSceneComponent
from './scenes/common/NarrationSceneComponent.vue';
import ChoiceSceneComponent
from './scenes/common/ChoiceSceneComponent.vue';
components: {
TitleScene: TitleSceneComponent,
NarrationScene: NarrationSceneComponent,
ChoiceScene: ChoiceSceneComponent,
},

I am thinking that you can use a v-if that loads different components depending on the data passed.
<TitleSceneComponent v-if="booleanValueOrCondition" />
<ChoiceSceneComponent v-if="anotherBooleanValueOrCondition" />
This way components can be loaded depending on your conditions.

I would use vue router to something like this: https://router.vuejs.org/guide/#html

You can do this:
<component
:is="sceneComp"
v-bind="options"
>
</component>
=============================
computed: {
sceneComp() {
return () => import(`./scenes/common/${this.scene}Component.vue`);
}

Related

vue multiple components in a single file

in vue documents I saw "Namespaced Components" in "script setup" guide it writes:
You can use component tags with dots like <Foo.Bar> to refer to components nested under object properties. This is useful when you import multiple components from a single file:
<script setup>
import * as Form from './form-components'
</script>
<template>
<Form.Input>
<Form.Label>label</Form.Label>
</Form.Input>
</template>
I wanted to know in this example what will the form-component look like, and what is the correct use case for such a component, does it have anything to do with "slot" or not.
In this case, form-components refers to a .js file that seems to be exporting single-file components (.vue).
form-components.js
export { default as Label } from './form-label.vue'
export { default as Input } from './form-input.vue'
You can then access these components via:
import * as Form from './form-components'
However, I recommend using a destructuring assignment methodology, as it is better interpreted by IDEs.
import { Input, Label } from './form-components'

Child components not rendering when referenced dynamically in composition API

I'm converting some components from vue 3's option API to the composition API. In this particular component I have two nested child components:
<script lang="ts" setup>
import ShiftOperation from "#/components/transformation-widgets/ShiftOperation.vue";
import RawJolt from "#/components/transformation-widgets/RawJolt.vue";
console.log([ShiftOperation, RawJolt])
...
From what I understand, if you're using the setup attribute in the script tag then all you have to do is import the component into a variable like I'm doing above and it should be available for the template without having to do anything else, like it's not like the old options api where you had to inject those components into the parent component.
Both components are imported successfully (confirmed by the console log:
When I'm rendering out this parent component I'm using the two child components to render out an array of data where I reference the children dynamically in the template based on information in each block of data that I'm iterating over:
<template>
<div class="renderer-wrapper">
<component
v-for="(block, index) in store.specBlocks"
v-bind:key="index"
:block="block"
:index="index"
:is="determineBlockComponent(block)"
#block-operation-updated="updateBlock"
>
</component>
</div>
</template>
// logic for determining the component to use:
export const determineBlockComponent = (block: JoltOperation) => {
switch (block.renderComponent) {
case 'shift':
return 'ShiftOperation'
default:
return 'RawJolt'
}
}
This worked fine in the options api version of it, but for some reason the components don't actually render. They show up in the elements tab:
But they don't show up in the view. I also added a created lifecycle hook into the child components that just console.log's out saying "created X", but those hooks don't fire.
Business logic wise nothing has changed, it's just been going from option api to composition api, so I'm assuming I'm missing some key detail.
Any ideas?
Your determineBlockComponent function should not return the string but the object of the component. Replace return 'ShiftOperation' with return ShiftOperation

Vue 3 use dynamic component with dynamic imports

I use Vue 3 and I have a dynamic component. It takes a prop called componentName so I can send any component to it. It works, kind of.
Part of the template
<component :is="componentName" />
The problem is that I still need to import all the possible components. If I send About as a componentName I need to import About.vue.
Part of the script
I import all the possible components that can be added into componentName. With 30 possible components, it will be a long list.
import About "#/components/About.vue";
import Projects from "#/components/Projects.vue";
Question
It there a way to dynamically import the component used?
I already faced the same situation in my template when I tried to make a demo of my icons which are more than 1k icon components so I used something like this :
import {defineAsyncComponent,defineComponent} from "vue";
const requireContext = require.context(
"#/components", //path to components folder which are resolved automatically
true,
/\.vue$/i,
"sync"
);
let componentNames= requireContext
.keys()
.map((file) => file.replace(/(^.\/)|(\.vue$)/g, ""));
let components= {};
componentNames.forEach((component) => { //component represents the component name
components[component] = defineAsyncComponent(() => //import each component dynamically
import("#/components/components/" + component + ".vue")
);
});
export default defineComponent({
name: "App",
data() {
return {
componentNames,// you need this if you want to loop through the component names in template
};
},
components,//ES6 shorthand of components:components or components:{...components }
});
learn more about require.context

how to import many vue component to my vue page?

i dont want to write a lot of import from.
import button1 from './components/button1'
import button2 from './componnets/button2'
import table1 from './componnets/table2'
...
Is there any good way to do it quickly?
How many way to do this thing?
following this pattern you can dynamically import components as well:
computed: {
comp () {
return () => import(`#/components/${this.componentName}.vue`)
}
}
and then use it like:
<template>
<component :is="comp"></component>
</template>
You could import and register the Vue Components globally in your index file:
import button1 from './components/button1'
Vue.component('button1', button1);
See the official documentation for more information: https://v2.vuejs.org/v2/guide/components-registration.html#Global-Registration
You may try require.context, look at the example in the official documentation, this should be enough to solve your problem. For more information about require.context, see this question.

Send props between Components (no related) React-Native

I have Components directory, there is js file Timer where I have countdown Component
const [countdownTimer, setCountdownTimer] = useState(15);
This component only returns <Text>{countdownTimer}</Text> on screen.
I also have another component Description. I want to handle this state in my Description component to make some changes after time has changed. The components aren't related (they aren't imported in each other)
I tried to import Description in Timer to send value as a props like
<Description setCountdownTimer={setCountdownTimer} />
and with style hide it but display: none isn't working on Android. I also try to just hide with another method
{false && <Description setCountdownTimer={setCountdownTimer} />}
but it's also not working, I got "undefined" in console.
For your case you should take a look at React Context: https://reactjs.org/docs/context.html
Hi #Danny If I understood your question correctly, you are trying to share the countDown state between two different components, and the way you try to do it may complicate you a bit, if not I suggest you use redux which is a library designed in order to manage the global states of applications, with redux you will be able to share the states of your application with several components.
You should need to create reducers and thanks to the createStore method from `redux you will be able to use a global state to your entire application
Here it's an example of code you can improve depending on your need :
import { createStore } from 'redux';
import reducer from './reducer';
import { Provider } from 'react-redux';
const store = createStore(reducer);
export default function App () {
return (
<Provider store = {store}>
<YourMainComponent />
</Provider>
)
}
You just need to wrapp your main component with the Provider component from 'react-redux' to access to your global state and manage it as you want. See more here
https://react-redux.js.org/introduction/basic-tutorial