Passing dynamic arbitrary values in Tailwind CSS 3/ Vue 3 - vue.js

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?

Related

How to access refs from app instance in Vue3

In Vue2 it was possible to access refs from the vue instance like so:
vm.$refs.someRef
How can I achieve this in Vue3? Access the refs from outside the app instance i.e from js code.
If you are using options API, it's the same. If you want to use composition API, you pass a ref to the template. It gets a little confusing because there are two different refs one is the attribute in the template (ref="myref") and the other is the function const myref = ref(null)
When used in the template, the ref value gets updated and can be then accessed via myref.value
from https://gitlab.com/mt55/maintegrity-fim-web-interface-server/-/jobs/3293032688/artifacts/download
<script setup>
import { ref, onMounted } from 'vue'
// declare a ref to hold the element reference
// the name must match template ref value
const input = ref(null)
onMounted(() => {
input.value.focus()
})
</script>
<template>
<input ref="input" />
</template>
If the ref is needed from outside of the app, it can be accessed through the instance with:
const app=createApp(App)
app._instance?.refs
however that only works if the ref is in the App component. For every other component, while the ref is available somewhere in the app object, traversing through the structure is much more complicated.

Find nearest parent Vue component of template ref (Vue 3)

When a Vue template ref is mounted, I want to get the nearest parent Vue component. This should be generic and work for any template ref so I've put it in a composition function (but that's just an implementation detail).
I had this working but my implementation used elem.__vueParentComponent while iteratively searching an element's ancestors. While reading the Vue source code I saw __vueParentComponent was only enabled for dev mode or if dev tools is enabled in production. Thus, I don't want to rely on that flag being enabled.
I thought this might be possible using vnodes but this isn't easily google-able. Here's an example of what I'm trying to do:
function useNearestParentInstance(templateRef) {
function getNearestParentInstance(el) {
// code here
}
onMounted(() => {
const el = templateRef.value;
const instance = getNearestParentInstance(el);
// do something with instance
});
}
<template>
<div>
<SomeComponent>
<div>
<div ref="myElem"></div>
</div>
</SomeComponent>
</div>
</template>
<script>
export default {
setup() {
const myElem = ref();
// nearest would be SomeComponent's instance in this case
useNearestParentInstance(myElem);
...
}
}
</script>
If you want the nearest vue parent you can simply use
ref().$parent // Not sure if syntax is same in vue3
ref().$parent will get the first vuecomponent that is the parent of the ref that you placed.

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

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'.

Access the component name from a Vue directive

I want to create a custom Vue directive that lets me select components on my page which I want to hydrate. In other words, this is what I want to archive
I render my Vue app on the server (ssr)
I attach a directive to some components, like this:
<template>
<div v-hydrate #click="do-something"> I will be hydrated</div>
</template>
I send my code to the client and only those components that have the v-hydrate property will be hydrated (as root elements) on the client.
I want to achieve this roughly this way:
I will create a directives that marks and remembers components:
import Vue from "vue";
Vue.directive("hydrate", {
inserted: function(el, binding, vnode) {
el.setAttribute("data-hydration-component", vnode.component.name);
}
});
My idea is that in my inserted method write a data-attribute to the server-rendered element that I can read out in the client and then hydrate my component with.
Now I have 2 questions:
Is that a feasible approach
How do I get the component name in el.setAttribute? vnode.component.name is just dummy code and does not exist this way.
PS: If you want to know why I only want to hydrate parts of my website: It's ads. They mess with the DOM which breaks Vue.
I could figure it out:
import Vue from "vue";
Vue.directive("hydrate", {
inserted: function(el, binding, vnode) {
console.log(vnode.context.$options.name); // the component's name
}
});
I couldn't get the name of my single file components using the previously posted solution, so I had a look at the source code of vue devtools that always manages to find the name. Here's how they do it:
export function getComponentName (options) {
const name = options.name || options._componentTag
if (name) {
return name
}
const file = options.__file // injected by vue-loader
if (file) {
return classify(basename(file, '.vue'))
}
}
where options === $vm.$options