Flakx
Flakx

Reputation: 1112

Vue.js $children by component name

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

Answers (7)

Asif Thebepotra
Asif Thebepotra

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

Armin
Armin

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

agm1984
agm1984

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

rjurado01
rjurado01

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

Dexygen
Dexygen

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

Linus Borg
Linus Borg

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

user4229130
user4229130

Reputation: 62

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

Upvotes: 3

Related Questions