Ref not working on custom component in Vue3 - vue.js

I'm using Vue3 with the composition API. In a form-component I put ref's on each field (child-component).
For some reason the ref's of the custom components are different from ref's for Quasar components.
When I console.log a ref to a custom component I get this in DevTools:
Proxy {__v_skip: true}
(without any properties in Target)
while a ref to a Quasar components gives this :
Proxy {…}
(with all properties of the component in Target)
For this reason I can't use the ref to access properties or methods of these child components.
I have no idea what __v_skip even means.
My custom components are defined with script setup, could that be a reason?
Any idea how to fix this?
UPDATE
If I use defineExpose in the child components for the properties and methods I want to access from outside with a ref, it does work. Not really handy though, since these components have lots of props.

Seem likes currently you cannot access the custom component by ref, if your component is written by Composition API (<script setup>). But you can try the way I mention underneath.
In the Vue 3 doc, there are some lines mentioned this behavior:
An exception here is that components using <script setup> are private
by default: a parent component referencing a child component using
<script setup> won't be able to access anything unless the child
component chooses to expose a public interface using the defineExpose
macro
Read more here: Vue 3 - Ref on Component
That means if you want to access anything from the custom component, your component has to expose that information. I think it's because in Vue 3 you don't need to have root component anymore, so if you define a ref, Vue does not know what the component you want to ref to.
But...
You can try to use yourRef.value.$el, maybe it will help.
Example:
// Parent.vue
<template>
<Child ref="childRef">
</template>
<script setup lang="ts">
// Import things...
const childRef = ref<InstanceType<typeof Child> | null>(null);
onMounted(() => {
console.log(childRef.value.$el);
});
</script>

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.

Vue3 Reactivity in script setup for translation

I am adding some DOM elements in the script setup side but I want the messages to change when I change the language. I am using vue-i18n plugin. It's easy to do it in the template section because I can basically use the useI18n().t method but how can I do this in the script setup section. Using the useI18n().t method doesn't ensure reactivity.
Example Code:
$(".time")[0].innerHTML = `
<div>0<span>${useI18n().t("details.hour")}</span></div>
<div>0<span>${useI18n().t("details.minute")}</span></div>
<div>0<span>${useI18n().t("details.second")}</span></div>
`
Manipulating DOM directly inside the script leads to inconsistence in your app, you should drive your component by different reactive data to achieve your goal.
In your current situation try to define a computed property based on the translation then render it inside the template based on its different properties :
<script setup>
const {t} =useI18n()
const time = computed(()=>{
return {
hour:t(""details.hour"),
minute:t(""details.minute"),
second:t(""details.second"),
}
})
</script>
<template>
<div class="time">
<div>0<span>{{time.hour}}</span></div>
<div>0<span>{{time.minute}}</span></div>
<div>0<span>{{time.second}}</span></div>
</div>
</template>

Vue 3 Mutate Composition API state from external script

Forewords
In Option API, I was able to directly mutate instance data properties without losing any of reactivity. As described in here.
If you ask why, well not everything is written in Vue and there're cases where external JS libraries have to change certain value inside Vue instance.
For example:
document.app = createApp({
components: {
MyComponent, //MyComponent is a Option API
}
})
//Somewhere else
<MyComponent ref="foo"/>
Then component state can be mutated as follow:
//Console:
document.app.$refs.foo.$data.message = "Hello world"
With the help of ref, regardless of component hiarchy, the state mutating process is kept simple as that.
Question
Now in Composition API, I want to achieve the same thing, with setup script if it's possible.
When I do console.log(document.app.$refs), I just only get undefined as returned result.
So let's say I have MyComponent:
<template>
{{message}}
<template>
<script setup>
const message = ref('Hello world');
</script>
How to mutate this child component state from external script? And via a ref preferably, if it's easier
Refs that are exposed from setup function are automatically unwrapped, so a ref can't be changed as a property on component instance.
In order for a ref to be exposed to the outside, this needs to be explicitly done:
setup(props, ctx) {
const message = ref('Hello world');
ctx.expose({ message });
return { message };
}
This is different in case of script setup because variables are exposed on a template but not component instance. As the documentation states:
An exception here is that components using are private by default: a parent component referencing a child component using won't be able to access anything unless the child component chooses to expose a public interface using the defineExpose macro
It should be:
<script setup>
...
const message = ref('Hello world');
defineExpose({ message });
</script>

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

Can't I use the mixins function of parent component in child component?

I am using the mixins function in the vuejs to code efficiently.
And then, I had a question.
I have imported mixins 'TTS' in the parent component.
And then the child component called the function of the tts.
However, the function was not called.
Is the parent component mixtns function not available in the child component?
import { tts } from "../components/mixins/tts/tts";
export default {
mixins: [tts]
}
This is importing mixins in the parent component.
<ion-row
class="drawer_middle_menu ion-align-items-center ion-justify-content-center"
#click="trySpeak($t('timeOut'))"
>{{$t('timeOut')}}</ion-row>
And this is a child component.
The 'trySpeak' function is a function in TTS that was imported by the parent component.
No, you would need to either pass as a prop or import directly into child component. With that said there are not recommended ways such as $parent but I would stay away from that.
See here for more info:
https://v2.vuejs.org/v2/guide/components-edge-cases.html