Reputation: 305
I have multiple input fields and I want to limit them to accept numbers only in Vue.js.
I want do disable user from typing any characters except digits from 0-9.
I already did that successfully by doing this(this solution copy-paste proof):
Code in Vue.js template:
<input type="text" name="priceMax" class="input" @input="correctNumberInput" />
Method that removes everything except numbers:
correctNumberInput: function(event){
event.target.value = event.target.value.replace(/[^0-9]/g, "");
}
This worked perfectly fine on multiple fields.
Here comes the problem: For different reason, I needed to use v-model on these input fields. After adding v-model my method doesn't work anymore. I guess it's because v-model also uses input event under the hood. So only adding "v-model", stops it from working:
<input type="text" name="priceMax" class="input" @input="correctNumberInput" v-model="priceMax" />
I have few possible solutions in mind, but all of them include a lot of repeated code.
For example, I could add watchers for every input field, but that would be a lot of repeated code (because I would need to do it for every input field). I have 5 input fields, so basically I would need to write 5 almost identical watchers. I would like to avoid that if that is possible... For example:
watch:{
number(){
this.priceMax = this.priceMax.replace(/[^0-9]/g, "");
}
}
Is there any way I can solve it and make it as simple as my solution was without repeating code? It would be nice to also have solution that is copy-paste proof. All suggestions are welcome! Thanks in advance!
Upvotes: 1
Views: 1069
Reputation: 2703
Maybe you can try this:
<input type="number" name="priceMax" class="input" @input="correctNumberInput" v-model.number="priceMax" />
From that site: click.
Upvotes: 1
Reputation: 2703
I've tried to test some code. Here what I have (link to the example):
<template>
<div>
<div>
<input
type="text"
name="priceMin"
class="input"
v-model="priceMin"
@input="correctNumberInput"
>
<label v-html="priceMin"></label>
</div>
<div>
<input
type="text"
name="priceMax"
class="input"
v-model="priceMax"
@input="correctNumberInput"
>
<label v-html="priceMax"></label>
</div>
</div>
</template>
<script>
export default {
name: "MyInput",
data: () => {
return {
priceMin: "",
priceMax: ""
};
},
methods: {
correctNumberInput: function(event, data) {
const name = event.target.name;
let value = String(this[name]).replace(/[^0-9]/g, "");
if (value) {
this[name] = parseInt(value, 10);
} else {
this[name] = "";
}
}
}
};
</script>
<style scoped>
input {
border: 1px solid black;
}
</style>
This is the code:
correctNumberInput: function(event, data) {
const name = event.target.name;
let value = String(this[name]).replace(/[^0-9]/g, "");
if (value) {
this[name] = parseInt(value, 10);
} else {
this[name] = "";
}
}
So I used your function, but I am not changing the event.target.value
, I am changing the data
. So I need to know the name of that data, that's why I use name
attribute from input fields (const name = event.target.name;
)
Update
If we have input type=number
, then it has strange (empty) value inside @input
callback. So it seems, better use keyboard filter (example here):
The main idea to have keyboard filter:
filterDigitKeys: function(event) {
const code = window.Event ? event.which : event.keyCode;
const isSpecial =
code === 37 ||
code === 39 ||
code === 8 ||
code === 46 ||
code === 35 ||
code === 36;
const isDigit = code >= 48 && code <= 57;
const isKeypad = code >= 96 && code <= 105;
if (!isSpecial && !isDigit && !isKeypad) {
// if not number or special (arrows, delete, home, end)
event.preventDefault();
return false;
}
}
And attach it to inputs:
<input type="number" min="0" name="numberInput" class="input"
v-model.number="numberInput" @keydown="filterDigitKeys">
Note: if we keep only @keydown
handler, then we will not filter text insert into our inputs (but ctrl+v is not working anyway, only by mouse).
Upvotes: 1