vue.jsvue-componentvuejs3refvue-composition-api

can't use template ref on component in vue 3 composition api


I want to get the dimensions of a vue.js component from the parent (I'm working with the experimental script setup).

When I use the ref inside a component, it works as expected. I get the dimensions:

// Child.vue
<template>
  <div ref="wrapper">
   // content ...
  </div>
</template>
<script setup>
import { ref, onMounted } from 'vue'

const wrapper = ref(null)

onMounted(() => {
  const rect = wrapper.value.getBoundingClientRect()
  console.log(rect) // works fine!
})
</script>

But I want to get the dimension inside the parent component. Is this possible?

I have tried this:

// Parent.vue
<template>
  <Child ref="wrapper" />
</template>
<script setup>
import Child from './Child'
import { ref, onMounted } from 'vue'

const wrapper = ref(null)

onMounted(() => {
  const rect = wrapper.value.getBoundingClientRect()
  console.log(rect) // failed!
})
</script>

the console logs this error message: Uncaught (in promise) TypeError: x.value.getBoundingClientRect is not a function


In the documentation I can only find the way to use template refs inside the child component

does this approach not work because the refs are "closed by default" as the rfcs description says?


Solution

  • I ran into this issue today. The problem is that, when using the <script setup> pattern, none of the declared variables are returned. When you get a ref to the component, it's just an empty object. The way to get around this is by using defineExpose in the setup block.

    // Child.vue
    
    <template>
      <div ref="wrapper">
       <!-- content ... -->
      </div>
    </template>
    
    <script setup>
    import { defineExpose, ref } from 'vue'
    
    const wrapper = ref(null)
    
    defineExpose({ wrapper })
    </script>
    

    The way you set up the template ref in the parent is fine. The fact that you were seeing empty object { } in the console means that it was working.

    Like the other answer already said, the child ref can be accessed from the parent like this: wrapper.value.wrapper.getBoundingClientRect().

    The rfc has a section talking about how/why this works: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0040-script-setup.md#exposing-components-public-interface

    It's also important to note that, with the <script setup> pattern, your ref in the parent component will not be a ComponentInstance. This means that you can't call $el on it like you might otherwise. It will only contain the values you put in your defineExpose.