Vasilije Bursac
Vasilije Bursac

Reputation: 305

How to limit multiple input fields (which use v-model) to only accept numbers in Vue.js (without repeating code for every input field)?

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

Answers (2)

Anton
Anton

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

Anton
Anton

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

Related Questions