I want to make a universal handler for input event and I need somehow to change v-model value in it. I tried this:
function validateInput(event) {
let input = event.target.value;
input = parseInt(input);
if(isNaN(input)) {
event.target.value = '';
} else {
event.target.value = input;
}
}
<input #input="validateInput($event)"
v-model="inputRef">
But inputRef doesn't react on event.target.value changes.
As Boussadjar mentioned, modifying event.target.value is no option.
just return the value from the event handler:
function onlyNumbersFilter(event) {
let input = event.target.value;
input = parseInt(input);
return isNaN(input) ? '' : input;
}
and use it:
<input #input="inputRef = onlyNumbersFilter($event)" :value="inputRef">
Or if you want to stick with v-model:
// setup
import { computed, ref } from 'vue'
// val is not DOM Event but value itself unwrapped from $event.target.value
function onlyNumbersFilter(val) {
const n = parseInt(val);
return isNaN(n) ? '' : val;
}
const filteredModel = function(valueRef, filterFn) {
return computed({
get() { return valueRef.value }
set(newVal) { valueRef.value = filterFn(newVal)) }
})
}
const input1Value = ref('')
const input1Model = filteredModel(input1Value, onlyNumbersFilter)
return {
input1Model
}
and usage:
<input v-model="input1Model">
In this case you should use value instead of v-model since v-model is the equivalent of #input ant value :
function validateInput(event) {
let input = event.target.value;
input = parseInt(input);
if(isNaN(input)) {
inputRef.value = '';
} else {
inputRef.value = input;
}
}
<input #input="validateInput($event)"
:value="inputRef">
I have a table with an input column. This input column will have value if it has been saved in ddbb before.
If this value changes, handled with an event '#blur', I show a modal to confirm the change.
My problem is that if you want to keep the old value it will always change...
I tried to change this value with javascript, but it doesn't work... Any suggestions?
This is my code:
<b-tbody>
<b-tr
v-for="(driver, index) in drivers"
:key="driver.clientId">
<b-td
data-label="Client"
:title="driver.clientName">{{ driver.clientName }}</b-td>
<b-td data-label="Numerator">
<b-input
:id="'numeratorDriver_'+index"
class="driver-numerator text-right"
type="text"
:value="(driver.driverNumerator === undefined) ? 0 : $utils.formatNumber(driver.driverNumerator)"
#blur="calculatePricingDriver($event, index)" /></b-td>
<b-td
class="text-right"
data-label="Pricing"
:title="driver.pricingDriver"><span>{{ driver.pricingDriver }}</span></b-td>
</b-tr>
</b-tbody>
<script>
function calculatePricingCustomDriver (event, index) {
let lastValue = this.drivers[index].driverNumerator
if (this.$store.getters.price !== undefined) {
let title = 'Modify numerator'
let message = 'If you modify numerator driver, the calculated pricing will be deleted'
this.$bvModal.msgBoxConfirm(message, {
title: title,
size: 'sm',
buttonSize: 'sm',
okTitle: 'Yes',
okVariant: 'primary',
cancelTitle: 'No',
cancelVariant: 'primary',
hideHeaderClose: false,
centered: true
})
.then(confirmed => {
if (confirmed) {
this.$http.get('delete-price', { params: { id: this.$store.getters.id } })
.then((response) => {
if (response.status === this.$constants.RESPONSE_STATUS_OK) {
this.price = ''
let newNumerator = event.target.value
this.drivers[index].driverNumerator = Number(newNumerator)
let sumTotal = _.sumBy(this.drivers, 'driverNumerator')
for (let i = 0; i < this.drivers.length; i++) {
this.drivers[i].pricingDriver = (this.drivers[i].driverNumerator / sumTotal).toFixed(2)
}
} else {
this.drivers[index].driverNumerator = lastValue
// this is that I want change because it doesn't work fine
document.getElementById('numeratorDriver_' + index).value = lastValue
}
})
} else {
this.drivers[index].driverNumerator = lastValue
document.getElementById('numeratorDriver_' + index).value = lastValue
}
})
.catch(() => {
/* Reset the value in case of an error */
this.$utils.showModalError()
})
} else {
let newNumerator = event.target.value
this.drivers[index].driverNumerator = Number(newNumerator)
let sumTotal = _.sumBy(this.drivers, 'driverNumerator')
for (let i = 0; i < this.drivers.length; i++) {
this.drivers[i].pricingDriver = (this.drivers[i].driverNumerator / sumTotal).toFixed(2)
}
}
}
</script>
how is calculatePricingCustomDriver being loaded into your Vue component? For it to be called like that from #blur you would need to define it as a method:
<template>
<!-- your table-->
</template>
<script>
export default {
name: "MyComponent",
methods : {
calculatePricingCustomDriver () {
// your code
}
}
}
</script>
Or it could be installed as a global mixin
use component, when i input content,it has a limit condation,only input number length is 8,and only 2 radix point,for example,can input 123.45, 123.456 is not.
<input :type="type"
v-model="inputValue"
:max-length="maxInputLength"
:maxlength="maxInputLength"
:placeholder="rightPlaceholder"
:class="['input', disabled ? 'input-style-disabled' : '']"
:disabled="disabled"
#input="onTextChange"/>
and data is :
data () {
return {
inputValue:'', //input value
}
},
mounted(){
if(this.value) {
this.inputValue = this.value;
}
},
methods:{
formatAmount(value){
let result;
if (value.indexOf(".") > 0 && value.length - value.indexOf(".") >= 4){
result = value.substr(0,value.indexOf('.') + 3);
// fValue = value.toFixed(2);
Log.d("formatAmount",result);
}else {
result = value;
Log.d("formatAmount else",result);
}
return result;
},
onTextChange() {
setTimeout(() => {
this.inputValue = this.formatAmount(this.inputValue);
this.$emit("onTextChange",this.inputValue);
}, 10);
}
}
totally,this just work when two point change to three point,then still can input three point.
how does it work,need help.thx.
You can use array split on (.) and then check arr[1].length<=2 and arr[0].length <=6.or use regex /^\d+(.\d{1,2})?$/ for 2 radix point;
I'm using vue-2.4 and element-ui 1.4.1.
Situation
I have a basic input which is linked with v-model to a computed property. When blur I check if the value input is greater or lower than min and max and I do what I have to do ... Nothing fancy here.
Problem
The value displayed in the input does not always equal enteredValue
Steps to reproduce
1) Input 60 --> Value displayed is the max so 50 and enteredValue is 50 (which is ok)
2) Click outside
3) Input 80 --> Value displayed is 80 and enteredValue is 50
Questions
How can I fix that so the value displayed is always the same as the enteredValue ?
Here is the minimal code to reproduce what I'm facing JSFIDDLE
<div id="app">
The variable enteredValue is {{enteredValue}}
<el-input v-model="measurementValueDisplay" #blur="formatInput($event)"></el-input>
</div>
var Main = {
data() {
return {
enteredValue: '',
max: 50,
min: 10
}
},
computed: {
measurementValueDisplay: {
get: function () {
return this.enteredValue + ' inchs'
},
set: function (newValue) {
}
},
},
methods: {
formatInput($event) {
let inputValue = $event.currentTarget.value;
if (inputValue > this.max) { this.enteredValue = this.max}
else if (inputValue < this.min) { this.enteredValue = this.min}
else this.enteredValue = inputValue
}
}
}
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')
Reading this vuejs, will understand what happens
"computed properties are cached based on their dependencies. A computed property will only re-evaluate when some of its dependencies have changed."
Changed some comportament of the code. Made run:
computed() method not works properly for update value in window. But if looks at console the value yes updated.
So, i remove computed (getter and setter), and put into data, without setter and getter( i dont like this in javascript).
var Main = {
data() {
return {
measurementValueDisplay:'fff',
enteredValue: '',
max: 50,
min: 10
}
},
computed: {
/*measurementValueDisplay: {
get: function () {
console.log('Computed was triggered so I assume enteredValue changed',this.enteredValue);
return this.enteredValue + ' inchs'
},
set: function (newValue) {
console.log('setter de qye', this.enteredValue);
}
},*/
},
methods: {
formatInput($event) {
this.enteredValue = 0;
let inputValue = $event.currentTarget.value;
console.log(inputValue);
if (inputValue > this.max) { this.enteredValue = this.max}
else if (inputValue < this.min) { this.enteredValue = this.min}
else this.enteredValue = inputValue
this.measurementValueDisplay = this.enteredValue + ' inchs'
console.log(this.enteredValue, 'oioioioio0');
}
}
}
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')
Your problem is that the values used in the computed property was not updated with the validation capping at 50 (Was 50, is now updated to 50, no need to recalculate), therefore v-model did not update the input.
I've edited your jsfiddle to use two computed properties:
One with an accessor to validate the entered value, one which returns the value with " inch" appended.
Here is the interesting part:
computed: {
measurementValueDisplay: {
get: function () {
return this.enteredValue
},
set: function (newValue) {
this.enteredValue = 0;
let inputValue = parseInt(newValue);
if(Number.isNaN(inputValue)){this.enteredValue = this.min}
else if (inputValue > this.max) { this.enteredValue = this.max}
else if (inputValue < this.min) { this.enteredValue = this.min}
else this.enteredValue = inputValue
}
},
valueWithInch(){
return this.enteredValue + " inch";
}
},
In case anybody still needs a hack for this one, you can use a value that will always change ( for example a timestamp )
var Main = {
data() {
return {
enteredValue: '',
max: 50,
min: 10,
now: 1 //line added
}
},
computed: {
measurementValueDisplay: {
get: function () {
return (this.now - this.now + 1 ) * this.enteredValue + ' inchs'; //line changed
},
set: function (newValue) {
this.now = Date.now(); //line added
}
},
},
methods: {
formatInput($event) {
let inputValue = $event.currentTarget.value;
if (inputValue > this.max) { this.enteredValue = this.max}
else if (inputValue < this.min) { this.enteredValue = this.min}
else this.enteredValue = inputValue
}
}
}
I have a text box and only want to accept numbers and a period "." when using VueJS. Can anyone help with code? I'm new to Vue.
You can write a Vue method and that method can be called on the keypress event. Check out this fiddle.
Update:
adding source code:
HTML
<div id="demo">
<input v-model="message" #keypress="isNumber($event)">
</div>
Vue.js
var data = {
message: 1234.34
}
var demo = new Vue({
el: '#demo',
data: data,
methods: {
isNumber: function(evt) {
evt = (evt) ? evt : window.event;
var charCode = (evt.which) ? evt.which : evt.keyCode;
if ((charCode > 31 && (charCode < 48 || charCode > 57)) && charCode !== 46) {
evt.preventDefault();;
} else {
return true;
}
}
}
});
You should change your input to type="number" to more accurately reflect your behaviour. You can then use the built-in Vue.js directive v-model.number.
Usage:
<input type="number" v-model.number="data.myNumberData"/>
This was my solution. Most of the answers here have been deprecated. Additionally, input values always return a string, even if you key a number. So because of that, some of the solutions here did not work for me.
In my case I didn't want a decimal point, but I added that into the array for the purpose of this thread.
<b-form-input v-model.number="quantity" #keypress="isNumber($event)" type="number"></b-form-input>
isNumber (evt: KeyboardEvent): void {
const keysAllowed: string[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.'];
const keyPressed: string = evt.key;
if (!keysAllowed.includes(keyPressed)) {
evt.preventDefault()
}
}
Short and easy understand.
HTML
<input #keypress="onlyNumber" type="text">
VUE JS
onlyNumber ($event) {
//console.log($event.keyCode); //keyCodes value
let keyCode = ($event.keyCode ? $event.keyCode : $event.which);
if ((keyCode < 48 || keyCode > 57) && keyCode !== 46) { // 46 is dot
$event.preventDefault();
}
}
A simple way to do this in one line:
IsNumber(event) {
if (!/\d/.test(event.key) && event.key !== '.') return event.preventDefault();
}
Here is a better way to handle the specific question asked (numbers and "dots" only) by setting v-restrict.number.decimal using the following directive. It also had some bonus code to support alpha only or alphanumeric. You could also only allow "dots" although I do not know why you would. It will not allow extra characters to "sneak through" if typing fast. It also supports copy/paste, delete, and some other keys users would expect to still work from an input:
Vue.directive('restrict', {
bind (el, binding) {
el.addEventListener('keydown', (e) => {
// delete, backpsace, tab, escape, enter,
let special = [46, 8, 9, 27, 13]
if (binding.modifiers['decimal']) {
// decimal(numpad), period
special.push(110, 190)
}
// special from above
if (special.indexOf(e.keyCode) !== -1 ||
// Ctrl+A
(e.keyCode === 65 && e.ctrlKey === true) ||
// Ctrl+C
(e.keyCode === 67 && e.ctrlKey === true) ||
// Ctrl+X
(e.keyCode === 88 && e.ctrlKey === true) ||
// home, end, left, right
(e.keyCode >= 35 && e.keyCode <= 39)) {
return // allow
}
if ((binding.modifiers['alpha']) &&
// a-z/A-Z
(e.keyCode >= 65 && e.keyCode <= 90)) {
return // allow
}
if ((binding.modifiers['number']) &&
// number keys without shift
((!e.shiftKey && (e.keyCode >= 48 && e.keyCode <= 57)) ||
// numpad number keys
(e.keyCode >= 96 && e.keyCode <= 105))) {
return // allow
}
// otherwise stop the keystroke
e.preventDefault() // prevent
}) // end addEventListener
} // end bind
}) // end directive
To use:
<!-- number and decimal -->
<input
v-model="test"
v-ep-restrict.number.decimal
...
/>
<!-- alphanumeric (no decimal) -->
<input
v-model="test2"
v-ep-restrict.alpha.number
...
/>
<!-- alpha only -->
<input
v-model="test3"
v-ep-restrict.alpha
...
/>
This can be modified to serve as a base for just about any scenario and a good list of key codes is here
I solved issue like yours via vue.js filters. First i created filter - let's say in filters.js file
export const JustDigits = () => {
Vue.directive('digitsonly', (el, binding) => {
if (/[\d\.]+/i.test(el.value)) {
console.log('ok');
} else {
let newValue = el.value.replace(/[a-zA-Z]+/ig, '');
el.value = newValue;
console.log('should fix', newValue);
binding.value = el.value;
}
});
};
Then in the component where this functionality is required i did:
import {
JustDigits
} from './filters';
JustDigits();
And then you are able to use this directive in template:
<input v-model="myModel"
v-digitsonly
type="text"
maxlength="4" class="form-control" id="myModel" name="my_model" />
Please note, that my regex may differ from what you need, feel free to modify it as well as this code line let newValue = el.value.replace(/[a-zA-Z]+/ig, ''); that removes characters from the string. I posted it just to show you one of the possible solutions vue.js provides to solve task like this.
Building on previous solutions, to prevent multiple decimal positions, also pass the v-model to the function:
<input v-model="message" v-on:keypress="isNumber($event, message)">
and modify the isNumber method as follows:
isNumber(event, message) {
if (!/\d/.test(event.key) &&
(event.key !== "." || /\./.test(message))
)
return event.preventDefault();
}
To limit the number of digits after the decimal add the following line into the isNumber method:
if (/\.\d{2}/.test(message)) return event.preventDefault();
The \d{2} limits the entry of two digits. Change this to \d{1} to limit to one.
As stated in other answers, this does not prevent the pasting of non-numeric data.
I needed my input to allow only digits, so no e symbol, plus, minus nor .. Vue seems funky and doesn't re-trigger #onkeypress for symbols like dot.
Here is my solution to this problem:
<input
onkeypress="return event.key === 'Enter'
|| (Number(event.key) >= 0
&& Number(event.key) <= 9"
type="number"
>
I am taking digits only, limiting from 0 to 9, but also I do want enable form submit on Enter, which would be excluded with the above approach - thus the enter.
You can handle this via simple html
<input type="number">
and in your app.css
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* this is for Firefox */
input[type=number] {
-moz-appearance: textfield;
}
Style code will remove ugly arrows from your number input field and yes it accepts dots
#Kalimah answer didn't work for me, but I liked the idea of using regex.
In my case I needed to filter out any non-digit character, including dots.
The below code worked for me, Vue 2.6.
<input type="text" v-model="variable" #input="cleanVariable" />
methods: {
cleanVariable(event) {
this.variable = event.target.value.replace(/[^0-9]/g, "");
}
<v-text-field class='reqField' label="NUMBER" #keypress="isNumber($event)"></v-text-field>
methods: {
isNumber: function(evt) {
evt = (evt) ? evt : window.event;
var charCode = (evt.which) ? evt.which : evt.keyCode;
if (charCode > 31 && (charCode < 48 || charCode > 57) && (charCode != 9)) {
evt.preventDefault();
} else {
return true;
}
`enter code here`
},
}
Why not using an external mask lib like vue-the-mask or cleave.js?
For example, with vue-the-mask you can easily use theirs directive like this:
<input type="text" name="some-name" id="some-id" v-model="some.value" v-mask="'##.##.##.##.###'">
You can use this library https://www.npmjs.com/package/vue-input-only-number
import onlyInt, { onlyFloat } from 'vue-input-only-number';
Vue.use(onlyInt);
Vue.use(onlyFloat);
<input type="text" v-int>
<input type="text" v-float>
Just evaluate if is nan and now you can prevent default
<input #keypress="isNumber">
isNumber (val) {
if (isNaN(Number(val.key))) {
return val.preventDefault();
}
}
I cannot the perfect solution as some work for input but not for copy&paste, some are the other way around. This solution works for me. It prevents negative numbers, typing "e", copy&paste "e" text.
I use mixin so I can be reused anywhere.
const numberOnlyMixin = {
directives: {
numericOnly: {
bind(el, binding, vnode) {
// console.log(el, binding);
// this two prevent from copy&paste non-number text, including "e".
// need to have both together to take effect.
el.type = 'number';
el.addEventListener('input', (e) => {
// console.log('input', e);
// console.log(el.validity);
return el.validity.valid || (el.value = '');
});
// this prevents from typing non-number text, including "e".
el.addEventListener('keypress', (e) => {
let charCode = (e.which) ? e.which : e.keyCode;
if ((charCode > 31 && (charCode < 48 || charCode > 57)) && charCode !== 46) {
e.preventDefault();
} else {
return true;
}
});
}
}
},
};
export {numberOnlyMixin}
In your component, add to your input.
<input v-model="myData" v-numericOnly />
There is input event which is more powerful/flexible than keypress, keydown or change that reacts on any type of change: program or user type such as key presses or paste events.
// Initial input state
let prevValue = ''
let prevSelectionStart = 0
function allowNumbersOnly(event) {
const input = event.target
let value = event.target.value
// Check if value is number
let isValid = +value == +value
if (isValid) {
// preserve input state
prevValue = value
prevSelectionStart = input.selectionStart
} else {
// restore previous valid input state.
// we have to fire one more Input event in order to reset cursor position.
var resetEvent = new InputEvent('input')
input.value = prevValue
input.selectionStart = prevSelectionStart
input.selectionEnd = prevSelectionStart
input.dispatchEvent(resetEvent)
}
}
<input type="text" oninput="allowNumbersOnly(event)">
The problem with type="number" and min="1" is that it allows typing or pasting the - sign, the e sign and the + sign.
The problem with the Math.abs(value) is that it replaces the whole typed in number with 0, which we don't want and is very frustrating.
The e.keyCode is very unreadable and deprecated.
The pure HTML method doesn't work in this case, you have to use JavaScript.
Remove the type="number" and do this:
function inputNumberAbs() {
var input = document.getElementsByTagName("input")[0];
var val = input.value;
val = val.replace(/^0+|[^\d.]/g, '');
input.value = val;
}
<input oninput="inputNumberAbs()">
Regex explanation:
^0+ removes all 0s from beginning
| OR operator
[^\d]. remove everything that is ^(NOT) a \d(NUMBER) or a
.(PERIOD)
This prevents the user to type or paste any of the not defined characters, like e, -, +, and all other characters that are not numbers.
If you don't need decimal numbers just remove . from regex.
You can use the number type in it:
<input type="number" class="yourCssClass" placeholder="someText" id="someId" />
and then, add the CSS required to remove the up/down spinners:
/* 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;
}