Vue3 Dynamic Refs
Wade Zimmerman

Wade Zimmerman @wadecodez

About: Code by day; blog by night

Location:
California
Joined:
Jun 5, 2021

Vue3 Dynamic Refs

Publish Date: Oct 9 '22
13 4

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.

Comments 4 total

  • Andrea Ferroni
    Andrea FerroniJan 23, 2023

    It seams to me that this is not so dynamic. Is "multiple" refs: exactly 3.

    In my mind dynamic means that the number of refs is not fixed in the code like the example given above, but generated via a :v-for directive, maybe caused by an array ajax result.

    Can you extend this example in a more complete way, please?
    Thank you

  • Charlène Bonnardeaux
    Charlène BonnardeauxSep 22, 2023

    I love your helper to define props! It's really elegant!

Add comment