1

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!

Vasilije Bursac
  • 185
  • 1
  • 2
  • 15
  • 1
    Maybe [this one](https://stackoverflow.com/questions/50566430/vue-js-how-to-restrict-special-characters-in-an-input-field) will help – mare96 Aug 25 '20 at 21:16
  • Thanks for answer! These solutions are good, but the biggest problem for me is repeating of code. 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... – Vasilije Bursac Aug 25 '20 at 22:03

2 Answers2

1

Maybe you can try this:

<input type="number" name="priceMax" class="input" @input="correctNumberInput" v-model.number="priceMax" />

From that site: click.

tony19
  • 125,647
  • 18
  • 229
  • 307
Anton
  • 2,669
  • 1
  • 7
  • 15
  • Thanks for answer! Unfortunately, this one doesn't help, because I want do disable user from typing any characters except digits from 0-9 and your solution will only typecast user input from String to Number. – Vasilije Bursac Aug 25 '20 at 21:36
1

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).

Anton
  • 2,669
  • 1
  • 7
  • 15
  • Thank you very very much! This is the solution I was looking for! Very clever and elegant. I just didn't know how to do it this way, because I am beginner in Vue.js and I didn't know about usage of "[]" with "this". I definitely learned something new through your answer! Thanks again! – Vasilije Bursac Aug 26 '20 at 10:39
  • 1
    @VasilijeBursac this is not from Vue, this is from JS itself. Any object is actually a map, so `a.b` and `a['b']` works the same. – Anton Aug 26 '20 at 10:44
  • Oh, I didn't know that it's the same thing. Thank you for amazing explanation! – Vasilije Bursac Aug 26 '20 at 11:01
  • Sorry if I bother you, but do you know why this code doesn't work for number input fields? If you try to type "e", "-" etc in number input field, it still shows that in field, it doesnt't replace that with "" (empty string). I don't understand why it doesn't work, because I used the same function on number fields before and it worked perfectly. Check out code with added number field: [example with added number input field](https://codesandbox.io/s/wizardly-fire-fbr9r?file=/src/components/MyInput.vue) – Vasilije Bursac Aug 26 '20 at 19:29
  • 1
    @VasilijeBursac I've added Update to my answer. Check it – Anton Aug 26 '20 at 20:36
  • Thanks for answer, that is definitely a good point! Basically, it had empty value inside @input callback and inside property in "data", but field wasn't empty. What solved problem for me is adding `event.target.value = event.target.value.replace(/[^0-9]/g, "");` before `let value = String(this[name]).replace(/[^0-9]/g, "");`.This just made sure that we filtered and cleared the field, too. I understand that this isn't the best solution, so I will try yours, too! Thanks for everything Anton. Best regards! – Vasilije Bursac Aug 26 '20 at 20:40