I’m having trouble creating a reference to an element and storing it inside an (non-reactive) object that I initialize like this:
const myObj = {
myRef: ref(null),
...
}
And in the template I have a similar situation:
<template>
...
<Comp :ref="(el) => { myObj.myRef = el }"/>
...
</template>
Now, if I try to access myObj.myRef
(after mounting) I get undefined
…
I am sure this is due to my lack of understanding of how ref and reactive are unboxed within templates. Can someone explain to me what are the values that I am actually working with within :ref="..."
?
2
Answers
Only top-level refs are auto-unwrapped in the template. For others (such as your one in an object) you still need to use
.value
.These are the docs: Vue Guide – Template Refs: Function Refs.
el
is an element reference or component instance:In this case, it is a component reference (see my note at the end of this answer).
myObj.myRef
is a ref, not auto-unwrapped (because it is not top-level). Hence, the ref itself gets overwritten, not its value. If you want to use this approach, you can set the ref usingmyObj.myRef.value = el
.I’m not quite sure what’s happening here, especially since you haven’t shown this code. Accessing
myObj.myRef
verbatim should give you the component instance (as you overwrote the ref with the reference). However, I suspect that you are actually using.value
(myObj.myRef.value
), which logically yieldsundefined
, given that a component instance has novalue
property by default (you could expose one, if you wanted to).Since you seem to be simply binding the reference to your ref, you don’t need the function syntax. You can just use the following instead:
Note the fact that I am binding it now, not just passing the name (
:ref
vsref
). This is required because it includes a property access and is therefore not just a name.However, I suspect that this is a simplified example and that there is more going on than this.
Now, in the comments you gave a full implementation and asked if could be improved.
(As I guessed earlier, there is more detail than in your question: you are actually accessing and saving an exposed ref on component, not just saving the reference itself.)
So, let’s see what you’re trying to accomplish:
You appear to want to have access to a template ref internal to a component.
Your current implementation does this by
defineExpose
),ref
attribute of the component, which saves the exposed template ref to the other ref.Here’s what we’ve got so far (given the implementation you linked to in the comments):
Comp.vue
App.vue
Playground 1
The component is fine, but the parent can be simplified quite a lot by using a computed ref instead of a function
ref
binding.The changes:
Import
computed
:Create a template ref for the component itself:
You can use whatever name you wish, as long as you use the same one in the template.
Change the
ref
property ofmsg
from a plain ref to a computed:This creates a computed ref where the value will be the exposed ref
inner
of the component if present, elsenull
.Then we simply bind the template ref for the component to the component:
These changes yield this:
Playground 2
Although this is actually two lines longer (the extra ref and blank line), it is much simpler. Instead of mutating an object in a function handler for a
ref
binding, there is a just a computed ref accessing the exposed property.This is of course all rather subjective. You may prefer the function binding, or the function approach but extracted to a method. Etc.
Note that a template ref for a component is that component’s component instance:
I’m not sure what you’re actually trying to achieve, but I felt that this was worth mentioning.
Only top-level refs are auto-magically unwrapped inside templates.
Your ref function will be called and will replace your ref altogether on mount. Trying to access
.value
on that would in all likelihood result inundefined
.If you want the behavior you described, you’d want
:ref="(el) => { myObj.myRef.value = el }"
.You can use the binding syntax, as others have suggested:
:ref="myObj.myRef"
.DOM refs are a bit tricky to work with, so I’ll recommend an alternative method (that I personnaly use):
reactive
around that object to have something nicer to work withThat would give you: