Vue3 Dynamic Refs

Wade Zimmerman - Oct 9 '22 - - Dev Community

Scenario

Imagine you have multiple clickable fields which have icons. You want to focus the input field no matter where you click in the parent element.

Problem

Depending on where you click, the $event.target changes. This means that you either have to use a clever way to find the intended target or take an elegant approach.

Vue2 Solution

The most elegant approach is to define a ref and access the ref corresponding to the element being clicked.

In Vue2 this would look like the following.

<template>
  <div @click="clickMe('f1')">
    <input ref="f1" />
    <Icon/>
  </div>
  <div @click="clickMe('f2')">
    <input ref="f2" />
    <Icon/>
  </div>
  <div @click="clickMe('f3')">
     <input ref="f3" />
     <Icon/>
  </div>
</template>

<script>
 export default {
   methods: {
     clickMe(refId) {
       var input = this.$refs[refId]
       input?.focus()
     }
   }
 }
</script>
Enter fullscreen mode Exit fullscreen mode

Vue3 Problem

If you are using Vue3 composition API, you'll notice achieving this behavior is not super obvious.

Methods no longer have access to an object which holds all the refs defined in a component. So you may end up settling for a less elegant solution. However, you don't have to.

Vue3 Solution

The solution is simple. Use an object where the property values are refs. Then you could update your template to something like the following.

<script setup>
import { ref } from 'vue';

const inputs = {
    f1: ref(),
    f2: ref(),
    f3: ref(),
}

function clickMe(refId) {
  var input = inputs[refId]?.value
  input?.focus()
}
</script>

<template>
  <div @click="clickMe('f1')">
    <input :ref="inputs.f1" />
    <Icon/>
  </div>
  <div @click="clickMe('f2')">
    <input :ref="inputs.f2" />
    <Icon/>
  </div>
  <div @click="clickMe('f3')">
     <input :ref="inputs.f3" />
     <Icon/>
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

Bonus: A Better Way to Define the Refs

Instead of writing ref over and over. You could write a method to define refs from an array. Since I'm using Lodash, I did something like this.

function defineRefs(refs) {
  return _.chain(refs).keyBy().mapValues(ref).value();
}
Enter fullscreen mode Exit fullscreen mode

Now I can rewrite my inputs definition like this:

<script setup>

import { defineRefs } from '@/helpers'

const inputs = defineRefs(['f1', 'f2', 'f3'])

//...
</script>
Enter fullscreen mode Exit fullscreen mode

Conclusion

Instead of using an event target to focus an element, you can use dynamic refs. Doing so allows you to reuse event handlers.

. . . . . . . . . . . . . . . . . . . .
Terabox Video Player