На основе Есть ли в 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.
У кого-нибудь есть идеи, как это исправить?
Я думаю, что лучшим способом было бы создать пользовательскую функцию фильтра, которая запускается при нажатии клавиши. С помощью собственного пользовательского фильтра вы также можете удалить 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;
}
}
Поскольку целое число состоит только из цифр, вы можете проверить только, является ли каждая нажатая клавиша цифрой, нет необходимости проверять все введенное значение.
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. Вы можете отключить их вот так.
спасибо за ваш ответ, это довольно хорошее решение. Но поддержка отрицательных целых чисел отсутствует :)
ах, ты прав. Вы можете разрешить клавишу «-», если это первая нажатая клавиша, а затем выполнить целочисленный тест, как в моем обновленном ответе.
Насколько я понимаю, у вас есть следующие требования:
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 мы можем разрешить, обновив регулярное выражение, я обновил свой ответ. Теперь он принимает отрицательные числа.
Согласно вашему комментарию к ответу @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;
}
В событии keyup
проверьте, является ли isAcceptingFloatingPointNumbers
ложным и введенный ввод не является целым числом, очистите значение поля ввода. Чтобы проверить, является ли входное значение целым числом или нет,
/^-?[0-9]+$/.test(num)
.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
и предотвратите дальнейшее выполнение.
Если пользователь пытается ввести число с плавающей запятой, вы можете вернуть целую часть этого числа с плавающей запятой, удалив дробные цифры с помощью метода 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>
Спасибо за ваш ответ. Я думаю, что это хорошее решение (и мне все равно на точку), но очистка поля, если ввод недействителен, - это не то, что я ищу :(
Если ввод недействителен, что вы хотите выполнить? Или вы хотите ограничить ввод чисел с плавающей запятой?
Я добавил еще одну логику, посмотрите, поможет ли это.
спасибо за Ваш ответ. Я думаю, что лучше придерживаться
type = "number"
, потому что тогда вам не придется проверять значения с плавающей запятой. Итак,filter
нужно заботиться только о целочисленных значениях (на основе логической опоры)