Home > Software design >  How to create a numeric integer input using Vuetify?
How to create a numeric integer input using Vuetify?

Time:01-12

Based on Is there specific number input component in Vuetify? I'm trying to create a numeric input.

The input and output value is unknown so it could be undefined or null because one might want to clear the field so it should not respond with 0.

The input component should not have "up"/"down" buttons if possible.

If the user passes in a flag isAcceptingFloatingPointNumbers = false this input should only accept integer values ( it should not be possible to type floats )

Reproduction link

<template>
  <v-app>
    <v-main>
      <v-text-field 
        type="number"
        label="number input" 
        :clearable="true"
        :model-value="num"
        @update:modelValue="num = $event"
      />
    </v-main>
  </v-app>
</template>

<script setup lang="ts">
import { ref, watch, Ref } from 'vue'

const num: Ref<unknown> = ref(undefined)

watch(num, () => console.log(num.value))
</script>

How can I make sure the user can only type integer values if the flag isAcceptingFloatingPointNumbers returns false? The only thing coming to my mind is to append a custom rule like

v => Number.isInteger(v) || 'Must be integer'

but AFAIK this rule would trigger even if the value could be undefined. Is there a way to prevent the user input instead?


Based on yoduh's answer I tried this ( reproduction link )

NumberField.vue

<template>
  <v-text-field 
    type="number"
    label="number input" 
    :clearable="true"
    :model-value="num"
    @update:modelValue="emit('update:modelValue', $event)"
    @keypress="filterInput"
  />
</template>

<script setup lang="ts">
const props = defineProps<{
  num: unknown;
  isAcceptingFloatingPointNumbers: boolean;
}>();

const emit = defineEmits<{
  (e: "update:modelValue", newValue: unknown): void;
}>();

function filterInput(inputEvent) {
  if(props.isAcceptingFloatingPointNumbers.value) {
    return true;
  }
  
  const inputAsString = inputEvent.target.value.toString()   inputEvent.key.toString();
  const inputValue = Number(inputAsString);
  
  if(!Number.isInteger(inputValue)) {
    inputEvent.preventDefault();
  }
  
  return true;
}
</script>

I'm consuming the component like so

<template>
  <number-field :num="num" :isAcceptingFloatingPointNumbers="false" @update:model-value="num = $event" />
</template>

<script setup lang="ts">
import { ref, watch } from 'vue'
import NumberField from "./NumberField.vue";

const num: Ref<unknown> = ref(undefined);
  
watch(num, () => console.log(num.value));
</script>

The problem is that my filter function is wrong. It's still possible to type "12.4" because the filter ignores "12." and then converts "12.4" to 124.

Does someone got any ideas how to fix this?

CodePudding user response:

I think the best way would be to create a custom filter function that runs on keypress. With your own custom filter you can also remove the type="number" since it's no longer necessary and will remove the up/down arrows on the input.

<v-text-field 
        label="number input" 
        :clearable="true"
        :model-value="num"
        @update:modelValue="num = $event"
        @keypress="filter(event)"
      />
const filter = (e) => {
  e = (e) ? e : window.event;
  const input = e.target.value.toString()   e.key.toString();

  if (!/^[0-9]*$/.test(input)) {
    e.preventDefault();
  } else {
    return true;
  }
}

updated sandbox

CodePudding user response:

Since an integer is made only of digits, you can test only if each pressed key is a digit, no need to check the whole input value.

function filterInput(inputEvent) {
  
  if(props.isAcceptingFloatingPointNumbers.value) {
    return true;
  }
  
  if(!Number.isInteger(Number(inputEvent.key))) {
    // Of course, you can choose any other method to check if the key 
    // pressed was a number key, for ex. check if the event.keyCode is 
    // in range 48-57.
    inputEvent.preventDefault();
  }
  
  return true;
}

Concerning the arrows, it is not a Vuetify specific element, but elements added by the browser to inputs of type number. You can disable them like this.

  • Related