Reputation: 1331
I am new to Vue and am trying to use $refs to grab some elements in the DOM from a sibling component (for very basic purposes, just to get their heights, etc.), and I'm doing so in a computed.
No matter what I try, this.$root.$refs
either always comes back undefined or as an empty object, and I don't know what I'm doing wrong.
In a parent component, I have:
<template>
<ComponentA />
<ComponentB />
</template>
In component A I have:
<template>
<div id="user-nav">
<div ref="nav-container">
<slot />
</div>
</div>
</template>
I just try to see if I can access this in ComponentB by console logging
console.log(this.$root.$refs);
in that component's mounted function.
But I keep getting an empty object.
Can you just not access things across sibling components like this???
Upvotes: 7
Views: 28667
Reputation: 413
Ok I faced this problem in 2 situations
1- mounted
hook was inside methods
which is worst typo
2- ref
was inside a v-for
loop which can not really be initialized without for loop being started in my case the for loop array was empty
Upvotes: 0
Reputation: 1
I had the same problem but the cause was another one:
I had a v-if
condition on the referenced element. So at the time of mounting the element was not rendered because the condition was still false.
After 3 hours of looking for the cause for the problem, it was so obvious.
<template>
<sub-component v-if="showCondition" ref="componentName">
<div>someContent</div>
<div>someMoreContent</div>
</sub-component>
</template>
<script lang="ts">
export default class MyComponent extends Vue {
private showCondition = false; // set to true after a REST request
mounted() {
console.log(this.$refs); // this was empty
}
}
</script>
This problem can be solved by moving the v-if to an additional template element inside the referenced component.
<template>
<sub-component ref="componentName">
<template v-if="showCondition">
<div>someContent</div>
<div>someMoreContent</div>
</template>
</sub-component>
</template>
<script lang="ts">
export default class MyComponent extends Vue {
mounted() {
console.log(this.$refs); // now 'componentName' is available
}
}
</script>
Upvotes: 0
Reputation: 2085
You might have already solved this, but just to answer this Q:
I had a similar problem and it seems that this happens because the function is being called before Vue has had time to replace the root DOM with its own version. What you can do to fix this is to create a mounted
life-cycle hook and call the function there.
const vm = new Vue({
el: '#app',
data: {
sayHi: 'Hello World',
},
mounted: function() { // The hook. Here, Vue has already worked its magic.
console.log('YOUR ELEMENT ----> ', this.doSomething())
},
methods: {
doSomething: function() {
return this.$refs['nav-container']
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<span ref="nav-container">{{ sayHi }}</span>
</div>
I found the solution here.
Upvotes: 7
Reputation: 11
In my specific case the
this.$refs
part referenced to several components. Accessing a specific component by reference was possible:
this.$refs[componentName]
To access a specific field of the component above I had to select the first observable:
this.$refs[componentName][0].someFieldToBeSelected -> this works
this.$refs[componentName].someFieldToBeSelected -> this fails
.
Upvotes: 0
Reputation: 3335
This might not be an answer to your particular problem but I discovered that another reason for $refs
being empty is that the component on which it is defined has not created/mounted yet. Probably due to the lazy rendering Vue uses.
I had a set of tabs, one of which was the one I had a "ref=" in, and if I did not visit that tab then $refs
was empty. But íf I first visited the tab then the ref was set up correctly.
Upvotes: 3
Reputation: 492
I had the same problem. I was using this.$refs
in computed property. I just got rid of computed properties and used methods and everything started to work properly because $refs are not reactive as mentioned for example here and here
Upvotes: 4
Reputation: 6739
You can do it, the problem you have is that you don't actually have any refs on your parent component.
I do not recommend doing this anyway either use vuex, an eventbus or $emit
Vue.component('componentA', {
template: '<div><span ref="ref-inside-a">You\'re in A!</span></div>',
methods:{
log(){
console.log('Hello from a')
}
}
})
Vue.component('componentB', {
props: ['ball'],
template: '<div><span>This is B!</span></div>',
mounted() {
this.$root.$refs['a'].log()
console.log(this.$root.$refs['a'].$refs['ref-inside-a'])
}
})
new Vue({
el: "#app",
mounted() {
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<component-a ref="a"></component-a>
<component-b ref="b"></component-b>
</div>
Upvotes: 0