Vue 3 use dynamic component with dynamic imports - vue.js

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

Related

How to watch deep state change in component using pinia

I'm having problems with watching deep changes inside component. My store looks like this (simplified):
import {defineStore} from 'pinia'
import {PiniaAuth} from "#/store/piniaauth";
import {get, getDatabase, ref, set, update} from "firebase/database";
import {toRaw} from 'vue'
import {PiniaMainStore} from "#/store/pinia";
// useStore could be anything like useUser, useCart
// the first argument is a unique id of the store across your application
export const PiniaInfo = defineStore('info', {
// arrow function recommended for full type inference
state: () => {
return {
info: {
name: 'John',
bill: 10000,
}
}
},
}
I need to watch bill changes and recount other data in component run methods. I've seen the function storeToRefs but didn't find how to watch if the data is in another object (in my case inside the info object).
Are there any ways to do this?

Passing dynamic arbitrary values in Tailwind CSS 3/ Vue 3

I know that you are unable to dynamically generate an arbitrary string in TailwindCSS like hue-rotate-[${randomHueColor}deg], because the arbitrary string has to exist at build time.
I see that it also seems impossible to generate the string in a different component and pass it through a prop to the component.
Eg.
<script setup>
import {ref, onBeforeMount} from 'vue'
import ImageComponent from './components/ImageComponent.vue'
const randomHue = ref('')
function generateRandomHue(){
let random = Math.floor(Math.random() * 361);
randomHue.value = String(`hue-rotate-[${random}deg]`) // or String(`filter: hue-rotate(${random}deg)`)
}
onBeforeMount(() => {
generateRandomHue()
})
</setup>
<template>
<ImageComponent :hueColor="randomHue" />
</template>
On the component side, I've tried both class: and style: (with filter:).
Is there another way to go about this, so I can have a truly dynamic random arbitrary hue-rotate?

How to dynamically import multiple Vue.js components at once?

I'm trying to import multiple components from a folder conditionally. The first example works, but I need to get it working conditionally like in the last example that doesn't work. Otherwise I'd have to always go and change which components should import manually, after adding one to the project folder.
I've also tried wrapping the imports into a function, but I can't get the import * as components syntax to work. Importing one component works fine, but not with * return () => import * as components '#/components/index'
This works
import * as components from '#/components/index'
export default {
components: {
...loadedComponents
}
}
index.js and electronIndex.js
export {
default as one
}
from './One.vue'
export {
default as two
}
from './Two.vue'
This of course doesn't work, but demonstrates what I need
I also tried importing conditionally via functions like stated above, but can't get that to work either.
const IPC = window.ipcRenderer
if (IPC) {
import * as components from '#/components/electronIndex'
} else {
import * as components from '#/components/index'
}
export default {
components: {
...loadedComponents
}
}

Vuetify override default prop value

Is there any way to change default value of a prop in a vuetify component?
For example lets say we have a component like v-btn.
This component has many props, One of them like outlined with default value of false.
Lets say i want is to change this default value to true forever in my application. Is there any way?
I was able to do that at the top of my app's entry point (before any Vue component creation).
/**
* [required imports]
* (you must somehow import VBtn component separately)
*/
Vue.use(Vuetify);
VBtn.options.props.outlined.default = true;
But this practice is called monkey patching and not encouraged to use, consider to use inheritance instead.
In my case I was trying to get component from Vue.options.components['VBtn'] but it didn't work.
So I monkey patched vue library too:
import Vue from "vue";
import Vuetify from 'vuetify'
export const vueComponentsImported: any = {};
export const vueComponentFnDefault = Vue.component.bind(Vue);
/** #see node_modules/vue/src/core/global-api/assets.js */
export const vueComponentFnModded = (id, component) => {
vueComponentsImported[id] = component;
return vueComponentFnDefault(id, component);
};
Vue.component = vueComponentFnModded;
Vue.use(Vuetify);
let VBtn = vueComponentsImported['VBtn'];
if (VBtn) {
VBtn.options.props.outlined.default = true;
}
(please feel free to edit this code if it doesn't work, I have much more lines in my app)
It doesn't make sense to do this,you could just replace '<v-btn' with '<v-btn outlined'.

Nuxt send data between 2 components

welcome to this topic. i recently tried to use the Nuxt framework to make my web-application but i ran into a problem.
In my default layout i have two components. a header component and a sidebar component. if i click on the hamburger icon in the header component the sidebar needs to get smaller or bigger depending on the hamburger icon state (true or false)
so to make it more complicated i don't want to use a prop to send it through the other component. i want to make it as a template so people can use it easy. can i transform a local component variable to a global variable other components can use?
so the code i have now is like this:
this is the index page
this is the header component
this is the sidebar component
as you can see i trigger the hamburgerstate on the header component page.
i want to access that state in the sidebarcomponent to so i can adjust the sidebar
the one thing that's IMPORTANT is that it needs to be as simple as possible so people who use this template later don't have to add unnecessary work
any possibilities this can work?
The simplest way to achieve a global variable is to set it as a state element and have a mutation for changing it. As your 'hambuger' is a boolean there is no need to pass parameters to the mutation making it all the easier.
You may want to have a named module in you store to handle this but I'll just put it in store/index.js for now.
export const state = () => ({
hamburger: true
})
export const mutations = {
changeHamburger (state) {
state.hamburger = !state.hamburger
}
}
Then in any page or component you can access that state element:
Component.vue
<script>
import { mapMutations } from 'vuex'
export default {
computed: {
hamburger () {
return this.$store.state.hamburger
}
},
methods: {
...mapMutations({
hamburgerChange: 'changeHamburger'
})
}
}
</script>
So this means you can now use the computed property 'hamburger' in your component and can change it by calling 'hamburgerChange', eg <v-btn #click="hamburgerChange">.