Vue JS - How to restrict special characters in an input field?
Asked Answered
J

5

14

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.

Justino answered 28/5, 2018 at 12:26 Comment(1)
Masked inputs github.com/vuejs/awesome-vue/blob/master/README.md#masked-inputSwarth
S
22

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>
Sosthenna answered 16/4, 2020 at 19:14 Comment(3)
How to allow others keyboard events like "delete", tab and spaces with your example? I am thinking in the method with preventDefault(). Thank you!Stylist
Just change the 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
This works great, with the exception of dead keys. I have some keys on my keyboard that I either have to press twice to have that character appear twice, press the key once and then space to only have the character on its own appear, or in conjunction with another key to form a character like à. The UI won't update itself correctly unless you type a valid character, at which point the invalid characters are removed. Do you have an idea on how to get around that?Combine
E
7

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)

Evadnee answered 28/5, 2018 at 14:20 Comment(2)
Yes. This approach of using watch worked for me. However, I used couple of additional events as well from vue-pattern-input component. Referred from npmjs.com/package/vue-pattern-inputJustino
Not works: "[Vue warn]: Avoid adding reactive properties to a Vue instance or its root $data at runtime - declare it upfront in the data option."Helbon
S
0

For anyone who's intrested to avoid also the underscore here's the regex :

/^\W$|\_|/
Scanderbeg answered 31/5, 2022 at 13:39 Comment(0)
P
0

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();
           }
        }
},                
Paigepaik answered 1/6, 2022 at 10:50 Comment(0)
Y
0

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

Yurt answered 23/2, 2023 at 22:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.