Search code examples
javascriptvue.jsref

How to pass a ref to a child input component with Vue?


I'm currently trying to pass a ref to get the value of the input (base-input component) on submit. You will find below the two components. With the console.log in handleSubmit, email is always undefined.

Thanks in advance for your help.

Parent component

<template>
  <form @submit.prevent="handleSubmit">
    <div class="flex flex-col mt-10">
      <form-label forInput="email" label="Email Address" />
      <base-input type="email" name="email" ref="email" />
    </div>
  </form>
</template>

<script>

import BaseInput from "../UI/BaseInput.vue";
export default {
  components: {
    BaseInput,
  },
  methods: {
    handleSubmit() {
      const email = this.$refs.email.value;
      console.log(email);
    },
  },
};
</script>

Child Input component

<template>
  <input
    :type="type"
    :name="name"
    :ref="name"
  />
</template>

<script>
export default {
  props: ["type", "name"],
};
</script>

Solution

  • If you want to access the value from the child's input field in the parent component, you need a way for data to flow from child to parent component, and that is done through emits.

    But wouldn't it be nice to be able to use v-model with your custom BaseInput component? The same way one would use form input binding?

    <input
      :value="text"
      @input="event => text = event.target.value">
    

    or v-model to simplify

    <input v-model="text">
    

    Something like this

    <BaseInput v-model="email" />
    

    Well, we can do just that. What you need is a modelValue prop and update:modelValue emit event.

    You can wrap both inside a writeable computed property for a cleaner and clearer syntax.

    const props = defineProps({
        modelValue: {
            type: String,
        },
    });
    
    const emit = defineEmits(['update:modelValue']);
    
    const internalValue = computed({
        get() {
            return props.modelValue;
        },
        set(value: string) {
            return emit('update:modelValue', value);
        },
    });
    

    When internalValue is set to a new value, it emits that event to the parent component and it syncs it through props.modelValue. Meaning, you can change props.modelValue in the parent component and the change would be reflected inside your custom component. And vice versa.

    I like this approach since it gives you a very natural way of reasoning about your component. Now, this concept isn't restricted to v-model per se, you can use it with any prop you want to synchronize to the parent component. Just use name prop, update:name emit in child component and use v-model:name in parent component.


    Resources: