Vuejs V-Model Support without need for value prop.

Vuejs V-Model Support without need for value prop.

Have you ever wished to create a component that supports the v-model directive, but works without it as well?

<input v-model="searchText">

turns into

<input
  v-bind:value="searchText"
  v-on:input="searchText = $event.target.value"
>

As you can see, in order to implement the support, you have to declare a prop variable called value and emit an event labeled input. And that's it. However, you will quickly find out that at this point the component indeed supports the v-model directive, but it doesn't work at all without it. That's often undesirable. For instance, imagine you'd like to create a custom search component that includes a text input. Since it's a mere extension of text input, it's reasonable that it should support v-model. But it is also reasonable that you'd like to be able to use it without it since the input inside would normally work straight away had it been a plain HTML element. Let's tackle this.

Solution: Optional v-model support

Let's start by creating a simple search component that will accept value as prop. If the user doesn't provide it, it's initiated to an empty value.

  props: {
    value: {
      type: String,
      default: "",
    },
  },

However, we can't use this prop directly in the input since that would mutate it which is not recommended. To circumvent this problem we'll create a clever computed value that will use the value prop if passed from the parent, or a custom local value otherwise. We'll make use of the extended computed property syntax where one can declare different functions for setter and getter of the computed function.

  data() {
    return {
      localValue: this.value,
    };
  },
  computed: {
    searchValue: {
      get() {
        return this.isValuePropSet() ? this.value : this.localValue;
      },
      set(value) {
        this.$emit("input", value);
        this.localValue = value;
      },
    },
  },
  methods: {
    isValuePropSet() {
      return (
        !!this.$options.propsData && this.$options.propsData.value !== undefined
      );
    },
  },

Let's first take a look at the getter. When retrieving the value, the isValuePropSet() method is invoked. This method returns true when the value prop was set by the parent, not initialized to empty string by the default property. So when it was set from the outside, we'll just return the value property and the component works as if it was implemented as a regular component with v-model support. However, when the value was not set, then the getter returns localValue instead. In the setter the current value is both emitted as an input event and stored in the localValue.

With this pattern, we can bind the clever searchValue computed property to the input as usual

<input v-model="searchValue" />

The search component works with v-model attached as well as without it. And that's it. Happy Coding!