2

Currently i try to use cleavejs to formatting thousand separator my input number, it's show strange behavior when i input 1000.123 it show 1,000.12 which is correct format, but my v-model value is 1000.123 not 1000.12. other people suggesting using v-model.lazy but it only updated when i leave my focus from input text.

Is there any other way to solve this issue without using v-model.lazy ?

This is my current component

<script setup lang="ts">
import { ref, watch } from 'vue'
const props = withDefaults(defineProps<Props>(), {
  type: 'text',
  required: false,
  readonly: false,
  disabled: false
})
const inputValue = ref(props.modelValue)
const emit = defineEmits<{
  (e: 'update:modelValue', value: string | number): void
}>()

watch(
  () => props.modelValue,
  () => {
    inputValue.value = numeric.format(props.modelValue)
  },
  {
    immediate: true
  }
)

watch(
  inputValue,
  () => {
    emit('update:modelValue', parseFloat(inputValue.value.toString().replace(/(\d+),(?=\d+(\D|$))/g, '$1')))
  },
  {
    immediate: true
  }
)
</script>

<template>
 <input
        class="form-input"
        v-model.lazy="inputValue"
        v-cleave="{ numeral: true, numeralThousandsGroupStyle: 'thousand' }"
        :placeholder="props.placeholder"
        :required="props.required"
        :readonly="props.readonly"
        :disabled="props.disabled"
      />
</template>

I expect v-model return the same value as cleavejs did without using .lazy

1 Answers1

3

First, you don't need use watch at all, and we won't emit on this setter because we wait Cleavejs event

const inputValue = computed({
  set: (text: string) => {},
  get: () => new Intl.NumberFormat('en-US').format(props.modelValue)
})

Second, emit raw value data from Cleavejs onValueChanged event

const emit = defineEmits<{
  (e: 'update:modelValue', value: number): void
}>()

const onValueChanged = (e: any) => {
  emit('update:modelValue', Number(e.target.rawValue))
}

Last, Update your input directive

v-cleave="{ numeral: true, numeralThousandsGroupStyle: 'thousand', onValueChanged: onValueChanged }"

Full Component example

<script setup lang="ts">
import { computed } from 'vue'

export interface Props {
  modelValue: number
}

const props = withDefaults(defineProps<Props>(), {})

const inputValue = computed({
  set: (text: string) => {},
  get: () => new Intl.NumberFormat('en-US').format(props.modelValue)
})

const emit = defineEmits<{
  (e: 'update:modelValue', value: number): void
}>()

const onValueChanged = (e: any) => {
  emit('update:modelValue', Number(e.target.rawValue))
}
</script>

<template>
  <input
    v-cleave="{ numeral: true, numeralThousandsGroupStyle: 'thousand', onValueChanged: onValueChanged }"
    v-model="inputValue"
  />
</template>
martiendt
  • 1,909
  • 1
  • 17
  • 27
  • Hi, thankyou. it's works but in your component example, you declare inputValue twice, i'm already edit that for your answer. I remove computed and only use ref, and still working properly – Feizhang210291 Jun 19 '23 at 02:58
  • @Feizhang210291 actually tried it and it's not work with formatting thousand separator. so we still need computed to formatting this separator – martiendt Jun 19 '23 at 06:52