[Vuejs]-Two way data binding with :model.sync when prop has get() and set()

0👍

Instead of using the .sync modifier you can support the v-model directive in your custom component. v-model is syntax sugar for a value prop and an input event.

To support v-model just make sure your custom component has a value prop and emits an input event with the new value: this.$emit('input', event.target.value).

Here is an example of a <BaseInput> component I use, it’s written in TypeScript:

<template>
  <input
    :type="type"
    :value="value"
    class="input"
    v-on="listeners"
  >
</template>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
  name: 'BaseInput',

  props: {
    type: {
      type: String,
      default: 'text',
    },

    value: {
      type: [String, Number],
      default: '',
    },

    lazy: {
      type: Boolean,
      default: false,
    },

    number: {
      type: Boolean,
      default: false,
    },

    trim: {
      type: Boolean,
      default: false,
    },
  },

  computed: {
    modelEvent(): string {
      return this.lazy ? 'change' : 'input'
    },

    parseModel(): (value: string) => string | number {
      return (value: string) => {
        if (this.type === 'number' || this.number) {
          const res = Number.parseFloat(value)
          // If the value cannot be parsed with parseFloat(),
          // then the original value is returned.
          return Number.isNaN(res) ? value : res
        } else if (this.trim) {
          return value.trim()
        }
        return value
      }
    },

    listeners(): Record<string, Function | Function[]> {
      return {
        ...this.$listeners,
        [this.modelEvent]: (event: HTMLElementEvent<HTMLInputElement>) =>
          this.$emit(this.modelEvent, this.parseModel(event.target.value)),
      }
  },
})
</script>

You can use it like so:

<BaseInput v-model="text" />

Leave a comment