Reputation: 1112
I'm trying to access a specific child by name. At the moment, because of where the child is, I'm calling the child by this:
this.$root.$children[0]
Which is ok as long as that child is always [0]
but it would be great if there’s a way to do something like:
this.$root.$children['detail']
I keep thinking $refs
might be the answer to my problem but can never find a way that it helps me.
Any ideas?
Upvotes: 39
Views: 85261
Reputation: 330
var childComponent = false;
$.each(this.$root.$children,function(i,child){
if(child.$route.name === component){
childComponent = child;
}
});
If you have specified 'name' in routes then you can use this solution.
Upvotes: 0
Reputation: 2141
All is pretty much the same, but in Vue 2 you need to use: <details ref="detailsChild"></details>
instead of v-ref .
Then all you need to do is use this.$refs.detailsChild;
and you can access any of it's properties.
Upvotes: 4
Reputation: 17142
I was trying to target some children last night. I was trying to call el.focus()
on an input. My problem was that I was trying to do it from an instance method that fired from a button click, and the input was in a 3rd party library AND I was wrapping that in another component.
The solution for me was to put a ref
on my wrapper component.
For example, if you have markup like this:
<my-dropdown ref="myDropdown"></my-dropdown>
Inside my-dropdown
, you could put another ref
on one of its children:
<template>
<div>
<my-library-wrapper ref="libWrapper"></my-library-wrapper>
</div>
</template>
Inside my-library-wrapper
, you could import in a library from node_modules
that has refs on it. Most libraries put refs on things so you can use those to target them.
Now you could start to target our example components here with code like this:
console.log(this.$refs.myDropdown);
console.log(this.$refs.myDropdown.$refs);
console.log(this.$refs.myDropdown.$refs.libWrapper);
this.$refs.myDropdown.$refs.libWrapper.$refs.someThing.focus();
this.$refs.myDropdown.$refs.libWrapper.$refs.someThing.click();
At first glance, that might seem weird, but the benefit of doing this compared to stuff like this.$refs.myDropdown.$children[0].$children[1].focus();
is that refs
are much less brittle. If you or someone else adds <divs>
into the markup later, the code using refs
will not break because Vue is finding those ref-named elements by name, not by relative-distance.
My recommendation is to put ref="something"
on something and do console.log(this.$refs.something.$refs);
and take a look what you can see, and while you're doing that, do console.log(this.$refs.something);
and see what kind of other things are available in there-- stuff like $attrs
and $children
and $el
.
Upvotes: 2
Reputation: 5535
You can use this property:
this.$root.$children[0].$options.name
For example:
this.$root.$children.find(child => { return child.$options.name === "name"; });
Upvotes: 36
Reputation: 12561
You don't necessarily need $refs
, in fact sometimes they are not feasible if you have deeply nested components. I've found this Q&A several times while searching, but finally decidedly to implement my own solution since I run into this situation pretty frequently. Don't balk at the old-school for loops, they are necessary for a couple of reasons, for one, I test for x<descendants.length
(rather than setting something such as len=descendants.length
up front, and testing against that) on every iteration as I'm pushing on to the stack in the second for loop.
First, usage:
let unPersonalizable = matchingDescendants(this, /a.checkimprintfiinformation$/, {first: true});
Implementation:
function matchingDescendants(vm, matcher, options) {
let descendants = vm.$children;
let descendant;
let returnFirst = (options || {}).first;
let matches = [];
for (let x=0; x<descendants.length; x++) {
descendant = descendants[x];
if (matcher.test(descendant.$vnode.tag)) {
if (returnFirst) {
return descendant;
}
else {
matches.push(descendant);
}
}
for (let y=0, len = descendant.$children.length; y<len; y++) {
descendants.push(descendant.$children[y]);
}
}
return matches.length === 0 ? false : matches;
}
Upvotes: 3
Reputation: 23988
Is this child you are talking about really a child of the component that you want to access it from? In this case, v-ref
is indeed the answer:
// in the code of the parent component, access the referenced child component like this:
this.$refs.detailsChild
<!-- Template of the parent component, assuming your child Component is called Details -->
<details v-ref:details-child></details>
relevant API Documentation: http://vuejs.org/api/#v-ref
Upvotes: 42