What's the best way to implement text-character restrictions in Vue JS ? I am looking to achieve with RegExp so that the user will not be allowed to type the symbols in the field.
I use a two pronged approach:
First is to use a watch
or computed
value with a setter, as Daniel recommends above. Besides handling keyboard input, it also handles input via drag and drop, paste, or whatever else the user comes up with.
Second is a keydown
handler. When using only a watched value, there is a slight delay in the UI. The restricted character is briefly displayed before being removed. For a more seamless user experience, the keydown
listener cancels the keyboard event for invalid input.
new Vue({
el: "#app",
data: {
name: "",
},
watch: {
name(val) {
this.name = val.replace(/\W/g, "");
},
},
methods: {
nameKeydown(e) {
if (/^\W$/.test(e.key)) {
e.preventDefault();
}
},
},
});
html {
font-family: sans-serif;
}
.demo {
display: flex;
justify-content: space-between;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<main id="app">
<p>Try typing spaces, punctuation, or special characters into each box to see the difference made by the key handler.</p>
<div class="demo">
<div>
<div>Without key handler:</div>
<input type="text" v-model="name" />
</div>
<div>
<div>With key handler:</div>
<input type="text" v-model="name" @keydown="nameKeydown($event)" />
</div>
</div>
</main>
if
block to test for whatever keys you want to cancel. Tab, Delete, and Backspace aren't canceled in my example, but Space is canceled. To allow spaces, you could change the regex to use [^ \w]
(anything other than space or a "word character") instead of what it currently uses: \W
(any non-word character). You would have to change that portion of both regular expressions. So, this.name = val.replace(/[^ \w]/g, "");
and if (/^[^ \w]$/.test(e.key)) {
–
Sosthenna Either using a watch
or a computed
value with setter and getter.
watch: {
myAlphaNumField(newVal) {
let re = /[^A-Z0-9]/gi;
this.$set(this, 'myAlphaNumField', newVal.replace(re, ''));
}
}
If you want the regex to match a pattern exactly, the trick is in defining the regular expression in such a way that it allows all the characters up to the last.
For example, if you want to have exactly 5 digits (#####
), the regex should be \d{1,5}
and not \d{5}
, because you need to be able to type the first four before that, though you would still use that later one to verify if the field is finished/complete/valid.
another example, for A#A#A#
would be [A-Z]([0-9]([A-Z]([0-9]([A-Z]([0-9])?)?)?)?)?
The point is, that each character after the first is optional, but only if the preceding character is available
one more example for ###-AA
: \d(\d(\d(\-(\w(\w)?)?)?)?)?
you can also use an existing library such as https://github.com/insin/inputmask-core (which does not include the vue extension), or https://github.com/niksmr/vue-masked-input (which does)
For anyone who's intrested to avoid also the underscore here's the regex :
/^\W$|\_|/
Currently this doesn't allow special case keys like deletes and arrows
if (/^\W$/.test(e.key)) {e.preventDefault();}
You can do this to allow custom keyCodes
allowOnlyNumbers(e) {
// allowedKeys Safari issues allowing keyCodes through regex - 8 = backspace, 46 = delete, 38 ArrowUp, 40 = ArrowDown, 37 = ArrowLeft, 39 = ArrowRight
let allowedKeys = [8, 46, 38, 40, 37, 39];
var keypressed = e.which || e.keyCode;
if (/[^0-9\s]+/.test(e.key)) {
if(allowedKeys.every( e => e !== keypressed)) {
e.preventDefault();
}
}
},
I've been stuck trying to get a consistent behavior preventing not allowed characters in input fields without blinking the undesired entry before removing it or blocking undesired behaviors together with unaccepted characters...
Using keypress/keydown
and testing against event.key
using event.preventDefault()
to block undesired characters proved to be the most inconsistent approach once it blocks functions like tabulation, backspace, and cut/copy/paste into those inputs...
The best behavior that I could achieve is a little hackish but I think will not affect performance in a way that will matter as much as user experience.
Basically, we use the input
event instead of keyboard' events because of the class used by the event instance, concatenating input.value
with event.data
as the value here will not have the last entry itself, if the concatenated result matches the regex passed to inform undesired characters we prevent the default behavior and after that reset input.value
replacing undesired characters from resulting string...
const regexString = "[^0-9]";
const replaceString = '';
const regex = new RegExp(regexString);
const replaceRegex = new RegExp(`${regexString}`, 'g')
const el = document.querySelector('input')
el.addEventListener('input', (event) => {
const value = event.target.value + event.data;
if (event.target && regex.test(value)) {
event.preventDefault();
event.target.value = value.replace(replaceRegex, replaceString);
}
});
if you are using vue you need to sync with v-model or it will force the last entry when you leave the field so you can manage this by a custom directive and force a custom input event to vue handle the v-model update like this:
Vue.directive('replace', {
inserted(el, binding, vnode) {
const regexString = binding.value // "[^0-9]";
const replaceString = '';
const regex = new RegExp(regexString);
const replaceRegex = new RegExp(`${regexString}`, 'g')
const el = document.querySelector('input')
el.addEventListener('beforeinput', (event) => {
const value = event.target.value + event.data;
if (event.target && regex.test(value)) {
event.preventDefault();
event.target.value = value.replace(replaceRegex, replaceString);
vnode.elm.dispatchEvent(new CustomEvent('input')); // sync with v-model
}
});
},
});
Note that we need to register the event on beforeinput
as we need to dispatch a new input event for vue to update v-model so if we register our event in input
like before we got an infinite loop
© 2022 - 2024 — McMap. All rights reserved.