Vue.js $children by component name
Asked Answered
W

7

39

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?

Wil answered 3/3, 2016 at 10:12 Comment(0)
A
42

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

Altonaltona answered 3/3, 2016 at 11:48 Comment(1)
For people coming to this using Vue v2, you would instead set the ref attribute in the template like this: <details ref="detailsChild"></details>. vuejs.org/v2/api/#refWalliw
L
36

You can use this property:

this.$root.$children[0].$options.name

For example:

this.$root.$children.find(child => { return child.$options.name === "name"; });
Lipocaic answered 13/12, 2016 at 12:36 Comment(6)
This just accesses the name of the first component in the $children array, which is not what OP is asking for.Walliw
How so? Your one line of code is accessing the name of the first element in the array. You haven't given any explanation for how this line of code could be used to solve OP's problem.Walliw
You can use find: this.$root.$children.find(child => { return child.$options.name === "name"; });. I will update the answer.Lipocaic
To select child by component name: this.$root.$children.find(child => { return child.$options._componentTag === "tabs-component"; });Babysit
I think for this one, you need to add variable name to your child like this.export default { name: ''}Nocuous
This answer helped me but this solution is not working now, Following solution worked for me var childComponent = false; $.each(this.$root.$children,function(i,child){ if(child.$route.name === component){ childComponent = child; } });Bagging
M
4

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.

Mush answered 2/1, 2019 at 12:17 Comment(1)
Suggest editing to detailsChild to fix javascript syntax. Or use this.$refs["details-child"] (assuming vue supports that)Murderous
L
3
this.$root.$children[0].constructor.name
Lancelot answered 8/9, 2016 at 7:22 Comment(1)
Please edit with more information. Code-only and "try this" answers are discouraged, because they contain no searchable content, and don't explain why someone should "try this".Metalanguage
T
3

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;
}
Tav answered 19/12, 2017 at 15:6 Comment(0)
N
2

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.

Naaman answered 3/4, 2019 at 16:18 Comment(0)
B
0
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.

Bagging answered 20/10, 2020 at 21:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.