How to pass a ref to a child input component with Vue?
Asked Answered
E

2

5

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>
Eldridge answered 27/10, 2022 at 12:32 Comment(3)
It looks like you are trying to ref the component itself which doesn't really make any sense. What are you trying to accomplish with the ref?Macruran
Trying to get the value of the input once we submit the form, but i agree it,s not a ref on the component itself, but a ref on the input inside the componentEldridge
What about using emit to emit the value from the child to the parent instead? vuejs.org/guide/components/events.html#event-argumentsConference
H
7

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:

Henleigh answered 27/10, 2022 at 19:36 Comment(1)
Thanks for the reply, i used the solution above, but i have now a better understanding of emits with your explanationEldridge
S
0

first the BaseInput is spelt wrong in the template.

Instead of

<base-input type="email" name="email" ref="email" />

you need to change it to

<BaseInput :type="'email'" :name="'email'" ref="email" />

A way better approach will be to use @emit()

Child.vue

<template>
  <input
    :type="type"
    :name="name"
    @change="$emit('inputContent', Content)"
    v-model="Content"
  />
</template>

<script>
export default {
  emits: ['inputContent'],
   data() {
        return {
            Content: '',
        }
    },
  props: ["type", "name"],
};
</script>

Don't forget to declare your props as strings. 😉

Parent.vue

<template>
  <BaseInput :type="'email'" :name="'email'" ref="email" @inputContent="handleSubmit"/>
</template>
<script>

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

Here is something about emits in the vue docs and how to use v-model

I hope this helps :)

Splanchnic answered 27/10, 2022 at 14:51 Comment(1)
Thanks, it worked that way, i didn't think about using emits to get the valueEldridge

© 2022 - 2024 — McMap. All rights reserved.