DEV Community

Valentin Prugnaud 🦊
Valentin Prugnaud 🦊

Posted on • Edited on

Wrap a text field in a component with Vue.js

This post has been updated: https://dev.to/valentinprgnd/custom-v-model-with-vuejs-update-3han

While answering questions on Stack Overflow, one question that comes back a lot is how to wrap a form input into a Vue component and still be able to use v-model amazing shorthand.

To answer this question, we need to understand how v-model works first.

What is v-model?

v-model is syntax sugar that Vue.js provides to add to things to our component declaration:

  • a :value data binding
  • an @input event handler

For example:

<MyComponent v-model="myVariable" />
Enter fullscreen mode Exit fullscreen mode

is essentially the same as:

<MyComponent :value="myVariable" @input="(value) => myVariable = value" />
Enter fullscreen mode Exit fullscreen mode

Wrapping a text input into a component

Here's a concrete example of how to wrap a basic text input into a Vue component:

<template>
  <div class="custom-input">
    <input v-model="localValue" placeholder="Enter your name">
  </div>
</template>

<script>
export default {
  name: "TextField",
  props: {
    value: String // 1. Receives the value as a prop
  },
  data() {
    return {
      localValue: ""
    };
  },
  created() {
    this.localValue = this.value; // 2. Copy the value in a local value variable

    // Set a watcher to emit the changes to the parent component
    this.$watch("localValue", value => {
      this.$emit("input", value);
    });
  }
};
</script>

Enter fullscreen mode Exit fullscreen mode

Multiple things happened here:

  1. We need to tell our component we are receiving a value prop from the parent component
  2. We need to copy the value of the value prop to a scoped variable of our component, here it's localValue
  3. We setup a watcher to watch changes on the localValue variable and emit these changes to the parent component using this.$emit

FAQ

Why set the watcher?

We set up the watcher here to make sure the parent component receives the updates on the input event (meaning when you type in the field) every time the localVariable is updated (itself receiving the result of the input event on the input field).

Why after copying the value?

We set the watcher after the initial assignment of the value to avoid creating an infinite loop when initializing the component with a existing value (using it in an edit form for example).


Here's a code sandbox demonstrating the whole process in a small Vue app:

Top comments (0)