i've got an image loader component, which is getting the image id as prop, normally, i should use the ref like this:
<template>
<img
ref="foo"
/>
</template>
<script setup>
const foo = ref(null);
onMounted( () => {
const img = (foo.value as HTMLImageElement);
if (img) {
img.addEventListener("load", () => fitImage(img));
}
});
</script>
But how do i do it with dynamic ref?
<template>
<img
:ref="dynamicRef()"
/>
</template>
<script setup>
const dynamicRef = () => {
return 'image'+props.imageId;
}
</script>
I've already tried to place this inside an array, like
const refs = [];
refs[dynamicRef] = ref(null)
also
const refs = ref([])
refs.value[dynamicRef] = ref(null);
But nothing seems to work
<user-data #change="setUserInfo"></user-data">
this is the child component where have used emits to pass data.
here is the method of parent component.
setUserInfo(data) {
this.obj.payment_details = data;
},
is it possible to bind data from the above method?
export default {
data: () => ({
dialog: false,
obj: new Expense(),
saveLoader: false,
}),
}
Here you have an example on how to emit data from child component to parent (using Vue3 Composition API script setup):
Parent:
<template>
<Comp #my-var="callback" />
{{ test }}
</template>
<script setup>
import { ref } from 'vue'
import Comp from './Comp.vue'
const test = ref('')
const callback = data => test.value = data
</script>
Child:
<template>
<button
v-text="'click'"
#click="doEmit()"
/>
</template>
<script setup>
const emits = defineEmits(['myVar'])
const doEmit = () => emits('myVar', 'emiting this data')
</script>
Check out the Playground
I am using Vite to build my Vue SFCs. I need to access the modelValue inside a function declared by the component. How do I do it?
Here is what I tried:
<script setup>
defineProps([ 'modelValue' ]);
defineEmits([ 'udpate:model-value' ]);
function portPlaceholder() {
// ERROR THROWN HERE, modelValue is not defined
if ( modelValue?.type === 'example' ) return 'something';
if ( modelValue?.type === 'other' ) return 'something-else';
return 'default-something';
}
</script>
<template>
Some input: <input type="text" :placeholder="portPlaceholder()" />
</template>
Try to assign the define props to a prop constant the use that constant to access the prop field, and use computed property instead of the function :
<script setup>
import {computed} from 'vue';
const props = defineProps([ 'modelValue' ]);
const emit = defineEmits([ 'udpate:model-value' ]);
const portPlaceholder = computed(()=>{
if ( props.modelValue?.type === 'example' ) return 'something';
if ( props.modelValue?.type === 'other' ) return 'something-else';
return 'default-something';
})
</script>
<template>
Some input: <input type="text" :placeholder="portPlaceholder" />
</template>
Please tell me why reactivity between unrelated components does not work:
ModalsController.js:
import { ref } from 'vue';
export const useModal = (init = false)=>{
const isShowModal = ref(init);
const openModal = () => {
isShowModal.value = true;
};
const closeModal = () => {
isShowModal.value = false;
};
return {
isShowModal, openModal, closeModal
}
}
Header.vue:
<template>
<button #click="openModal">OpenModal</button>
{{isShowModal}}
<button #click="closeModal">CloseModal</button>
</template>
<script setup>
import {useModal} from "./ModalsController.js";
const { isShowModal,openModal,closeModal } = useModal();
</script>
Modal.vue:
<template>
<div v-if="isShowModal"> Modal window </div>
</template>
<script setup>
import {useModal} from "./ModalsController.js";
const {isShowModal} = useModal();
</script>
And everything works if I create a simple variable instead of a function like this:
ModalsController.js:
import { ref } from 'vue';
export const isShowModal = ref(false);
and accordingly, I change it in the header. But this is very inconvenient because there are way more functions (switching, etc.)
Thank you all in advance for your help. I put the code in the Playground for the test:
Not a working (func)
working (simple var)
The problem is useModal() creates a new ref() every time it's called. Each of your components calls useModal() to get the isShowModal ref, but each ref is a newly created one independent from each other.
To share the refs between components, move the ref creation outside of the useModal function definition:
import { ref } from 'vue';
const isShowModal = ref(false); 👈
export const useModal = (init = false) => {
// const isShowModal = ref(init); ❌ move this outside function
⋮
}
demo
I want to get the dimensions of a vue.js component from the parent (I'm working with the experimental script setup).
When I use the ref inside a component, it works as expected. I get the dimensions:
// Child.vue
<template>
<div ref="wrapper">
// content ...
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const wrapper = ref(null)
onMounted(() => {
const rect = wrapper.value.getBoundingClientRect()
console.log(rect) // works fine!
})
</script>
But I want to get the dimension inside the parent component. Is this possible?
I have tried this:
// Parent.vue
<template>
<Child ref="wrapper" />
</template>
<script setup>
import Child from './Child'
import { ref, onMounted } from 'vue'
const wrapper = ref(null)
onMounted(() => {
const rect = wrapper.value.getBoundingClientRect()
console.log(rect) // failed!
})
</script>
the console logs this error message:
Uncaught (in promise) TypeError: x.value.getBoundingClientRect is not a function
In the documentation I can only find the way to use template refs inside the child component
does this approach not work because the refs are "closed by default" as the rfcs description says?
I ran into this issue today. The problem is that, when using the <script setup> pattern, none of the declared variables are returned. When you get a ref to the component, it's just an empty object. The way to get around this is by using defineExpose in the setup block.
// Child.vue
<template>
<div ref="wrapper">
<!-- content ... -->
</div>
</template>
<script setup>
import { defineExpose, ref } from 'vue'
const wrapper = ref(null)
defineExpose({ wrapper })
</script>
The way you set up the template ref in the parent is fine. The fact that you were seeing empty object { } in the console means that it was working.
Like the other answer already said, the child ref can be accessed from the parent like this: wrapper.value.wrapper.getBoundingClientRect().
The rfc has a section talking about how/why this works: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0040-script-setup.md#exposing-components-public-interface
It's also important to note that, with the <script setup> pattern, your ref in the parent component will not be a ComponentInstance. This means that you can't call $el on it like you might otherwise. It will only contain the values you put in your defineExpose.
I don't this this is necessarily related to the <script setup> tag. Even in the standard script syntax your second example will not work as-is.
The issue is you are putting ref directly on the Child component:
<template>
<Child ref="wrapper" />
</template>
and a ref to a component is NOT the same as a ref to the root element of that component. It does not have a getBoundingClientRect() method.
In fact, Vue 3 no longer requires a component to have a single root element. You can define your Child component as :
<template>
<div ref="wrapper1">// content ...</div>
<div ref="wrapper2">// content ...</div>
</template>
<script >
import { ref } from "vue";
export default {
name: "Child",
setup() {
const wrapper1 = ref(null);
const wrapper2 = ref(null);
return { wrapper1, wrapper2 };
},
};
</script>
What should be the ref in your Parent component now?
Log the wrapper.value to your console from your Parent component. It is actually an object of all the refs in your Child component:
{
wrapper1: {...}, // the 1st HTMLDivElement
wrapper2: {...} // the 2nd HTMLDivElement
}
You can do wrapper.value.wrapper1.getBoundingClientRect(), that will work fine.
You could get access to the root element using $el field like below:
<template>
<Child ref="wrapper" />
</template>
<script setup>
import Child from './Child'
import { ref, onMounted } from 'vue'
const wrapper = ref(null)
onMounted(() => {
const rect = wrapper.value.$el.getBoundingClientRect()
console.log(rect)
})
</script
Right, so here's what you need to do:
// Parent component
<template>
<Child :get-ref="(el) => { wrapper = el }" />
</template>
<script setup>
import Child from './Child.vue';
import { ref, onMounted } from 'vue';
const wrapper = ref();
onMounted(() => {
const rect = wrapper.value.getBoundingClientRect()
console.log(rect) // works fine!
});
</script>
and
// Child component
<template>
<div :ref="(el) => { wrapper = el; getRef(el)}">
// content ...
</div>
</template>
<script setup>
import { defineProps, ref, onMounted } from 'vue';
const props = defineProps({
getRef: {
type: Function,
},
});
const wrapper = ref();
onMounted(() => {
const rect = wrapper.value.getBoundingClientRect()
console.log(rect) // works fine!
});
</script>
To learn why, we need to check Vue's documentation on ref:
Vue special-attribute 'ref'.
On dynamic binding of (template) ref, it says:
<!-- When bound dynamically, we can define ref as a callback function,
passing the element or component instance explicitly -->
<child-component :ref="(el) => child = el"></child-component>
Since the prop lets you pass data from the parent to a child, we can use the combination of the prop and dynamic ref binding to get the wanted results. First, we pass the dynamic ref callback function into the child as the getRef prop:
<Child :get-ref="(el) => { wrapper = el }" />
Then, the child does the dynamic ref binding on the element, where it assigns the target el to its wrapper ref and calls the getRef prop function in that callback function to let the parent grab the el as well:
<div :ref="(el) => {
wrapper = el; // child registers wrapper ref
getRef(el); // parent registers the wrapper ref
}">
Note that this allows us to have the ref of the wrapper element in both the parent AND the child component. If you wished to have access to the wrapper element only in the parent component, you could skip the child's callback function, and just bind the ref to a prop like this:
// Child component
<template>
<div :ref="getRef">
// content ...
</div>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
getRef: {
type: Function,
},
});
</script>
That would let only the parent have the ref to your template's wrapper.
If you're seeing the wrapper.value as null then make sure the element you're trying to get the ref to isn't hidden under a false v-if. Vue will not instantiate the ref until the element is actually required.
I realize this answer is not for the current question, but it is a top result for "template ref null vue 3 composition api" so I suspect more like me will come here and will appreciate this diagnosis.