Как создать числовой целочисленный ввод с помощью Vuetify?

На основе Есть ли в Vuetify специальный компонент для ввода чисел? Я пытаюсь создать числовой ввод.

Входное и выходное значение равно unknown, поэтому оно может быть undefined или null, потому что может потребоваться очистить поле, чтобы оно не должно было отвечать 0.

Компонент ввода не должен иметь кнопок «вверх»/«вниз», если это возможно.

Если пользователь передает флаг isAcceptingFloatingPointNumbers = false, этот ввод должен принимать только целые значения (не должно быть возможности вводить числа с плавающей запятой)

Ссылка на репродукцию

<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.info(num.value))
</script>

Как я могу убедиться, что пользователь может вводить только целочисленные значения, если флаг isAcceptingFloatingPointNumbers возвращает false? Единственное, что приходит мне в голову, это добавить пользовательское правило, например

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

но, насколько мне известно, это правило сработает, даже если значение может быть undefined. Есть ли способ предотвратить ввод данных пользователем?


Основываясь на ответе yoduh Я попробовал это ( ссылка на воспроизведение )

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>

Я потребляю компонент так

<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.info(num.value));
</script>

Проблема в том, что моя функция фильтра неверна. По-прежнему можно ввести «12,4», потому что фильтр игнорирует «12». а затем преобразует «12,4» в 124.

У кого-нибудь есть идеи, как это исправить?

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
0
139
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Я думаю, что лучшим способом было бы создать пользовательскую функцию фильтра, которая запускается при нажатии клавиши. С помощью собственного пользовательского фильтра вы также можете удалить type = "number", так как он больше не нужен, и удалит стрелки вверх/вниз на входе.

<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;
  }
}

обновленная песочница

спасибо за Ваш ответ. Я думаю, что лучше придерживаться type = "number", потому что тогда вам не придется проверять значения с плавающей запятой. Итак, filter нужно заботиться только о целочисленных значениях (на основе логической опоры)

baitendbidz 10.01.2023 11:11
Ответ принят как подходящий

Поскольку целое число состоит только из цифр, вы можете проверить только, является ли каждая нажатая клавиша цифрой, нет необходимости проверять все введенное значение.

function filterInput(inputEvent) {
  
  if (props.isAcceptingFloatingPointNumbers.value) {
    return true;
  }

  if (!inputEvent.target.value.length && inputEvent.key === '-'){
    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;
}

Что касается стрелок, это не особый элемент Vuetify, а элементы, добавленные браузером к вводам типа number. Вы можете отключить их вот так.

спасибо за ваш ответ, это довольно хорошее решение. Но поддержка отрицательных целых чисел отсутствует :)

baitendbidz 12.01.2023 12:17

ах, ты прав. Вы можете разрешить клавишу «-», если это первая нажатая клавиша, а затем выполнить целочисленный тест, как в моем обновленном ответе.

Igor Moraru 12.01.2023 13:42

Насколько я понимаю, у вас есть следующие требования:

  • Чтобы предотвратить пользовательский ввод на основе значения флага isAcceptingFloatingPointNumbers (принимайте только целые числа, если флаг установлен, поле false else должно принимать плавающие числа).
  • Нет стрелок вверх/вниз в поле ввода.
  • Поле ввода должно принимать значение 0.

Если мое понимание выше верно, вы можете просто выполнить это требование с помощью обычного текстового поля и для каждого события keyup. Вы можете заменить входное значение пустой строкой, если оно не соответствует переданному действительному regEx.

Живая демонстрация:

const { ref } = Vue;
const { createVuetify } = Vuetify;
const vuetify = createVuetify();

let options = {
    setup: function () {
    let num = ref('');
    let isAcceptingFloatingPointNumbers = ref(false);
    
    const validateInput = () => {
      const numbersRegEx = !isAcceptingFloatingPointNumbers.value ? /[^-\d]/g : /[^-\d.]/g;
      num.value = num.value.replace(numbersRegEx, '');
    }

    return {
      num,
      validateInput
    };
  }
};

let app = Vue
.createApp(options)
.use(vuetify)
.mount('#app');
<script src = "https://unpkg.com/vue@next/dist/vue.global.js"></script>
<script src = "https://unpkg.com/@vuetify/[email protected]/dist/vuetify.js"></script>
<link rel = "stylesheet" href = "https://unpkg.com/@vuetify/[email protected]/dist/vuetify.css"/>

<div id = "app">
  <v-text-field label = "Numper Input" v-model = "num" v-on:keyup = "validateInput"></v-text-field>
</div>

спасибо за ваш ответ, но, к сожалению, решение не поддерживает отрицательные целые числа :(

baitendbidz 12.01.2023 12:18

@baitendbidz мы можем разрешить, обновив регулярное выражение, я обновил свой ответ. Теперь он принимает отрицательные числа.

Rohìt Jíndal 12.01.2023 14:43

Согласно вашему комментарию к ответу @yoduh, если вы хотите придерживаться type = "number" (хорошо, чтобы уменьшить шаг для проверки нечисловых символов), затем скройте стрелки, используя следующий CSS-

/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

/* Firefox */
input[type=number] {
  -moz-appearance: textfield;
}

Логика 1-

В событии keyup проверьте, является ли isAcceptingFloatingPointNumbers ложным и введенный ввод не является целым числом, очистите значение поля ввода. Чтобы проверить, является ли входное значение целым числом или нет,

  1. Вы можете использовать шаблон регулярного выражения, /^-?[0-9]+$/.test(num).
  2. Вы можете использовать метод JS Number.isInteger(num).

Хотя во втором методе входное значение всегда будет строкового типа (почему?). Чтобы решить эту проблему, используйте встроенную директиву Vue.js v-model.number, чтобы преобразовать тип входного значения в число.

Демо-

const { ref } = Vue;
const { createVuetify } = Vuetify;
const vuetify = createVuetify();

let options = {
  setup: function() {
    let num = ref(null);
    let error = ref('');
    let isAcceptingFloatingPointNumbers = ref(false);
    const validateInput = () => {
      // If floats not allowed and input is not a integer, clean it.
      if (
        !isAcceptingFloatingPointNumbers.value &&
        !Number.isInteger(num.value)
      ) {
        num.value = null;
        error.value = "Only integers are allowed."
      } else {
        error.value = '';
      }
    };
    return {
      num,
      error,
      validateInput,
    };
  },
};

let app = Vue.createApp(options)
  .use(vuetify)
  .mount("#app");
.error {
  color: red;
}
<script src = "https://unpkg.com/vue@next/dist/vue.global.js"></script>
<script src = "https://unpkg.com/@vuetify/[email protected]/dist/vuetify.js"></script>
<link rel = "stylesheet" href = "https://unpkg.com/@vuetify/[email protected]/dist/vuetify.css"/>
<div id = "app">
  <v-text-field 
    type = "number"
    label = "number input" 
    :clearable = "true"
    v-model.number = "num"
    @keyup = "validateInput"
    >
  </v-text-field>
  <label class = "error">{{ error }}</label>
</div>

Единственный сбой здесь заключается в том, что если пользователь вводит 123. и перестает печатать, то dot будет виден из-за type = "number", но если вы используете это значение, оно всегда будет декодироваться как 123.
Если вы хотите ограничить ввод dot, определите ключ в событии keypress и предотвратите дальнейшее выполнение.

РЕДАКТИРОВАТЬ------------------

Логика 2

Если пользователь пытается ввести число с плавающей запятой, вы можете вернуть целую часть этого числа с плавающей запятой, удалив дробные цифры с помощью метода Math.trunc(num).

Демо-

const { ref } = Vue;
const { createVuetify } = Vuetify;
const vuetify = createVuetify();

let options = {
  setup: function() {
    let num = ref(null);
    let error = ref('');
    let isAcceptingFloatingPointNumbers = ref(false);
    const validateInput = () => {
      if (!isAcceptingFloatingPointNumbers.value && !Number.isInteger(num.value)) {
        error.value = "Only integer is allowed.";
        // Keep only integer part.
        num.value = Math.trunc(num.value);
      } else {
        error.value = ''
      }

    };
    return {
      num,
      error,
      validateInput,
    };
  },
};

let app = Vue.createApp(options)
  .use(vuetify)
  .mount("#app");
.error {
  color: red;
}
<script src = "https://unpkg.com/vue@next/dist/vue.global.js"></script>
<script src = "https://unpkg.com/@vuetify/[email protected]/dist/vuetify.js"></script>
<link rel = "stylesheet" href = "https://unpkg.com/@vuetify/[email protected]/dist/vuetify.css"/>
<div id = "app">
  <v-text-field 
    type = "number"
    label = "number input" 
    :clearable = "true"
    v-model.number = "num"
    @keyup = "validateInput"
    >
  </v-text-field>
  <label class = "error">{{ error }}</label>
</div>

Спасибо за ваш ответ. Я думаю, что это хорошее решение (и мне все равно на точку), но очистка поля, если ввод недействителен, - это не то, что я ищу :(

baitendbidz 12.01.2023 12:26

Если ввод недействителен, что вы хотите выполнить? Или вы хотите ограничить ввод чисел с плавающей запятой?

Neha Soni 12.01.2023 12:48

Я добавил еще одну логику, посмотрите, поможет ли это.

Neha Soni 12.01.2023 13:10

Другие вопросы по теме