2

Here is my implementation using Numeral.js:

        Vue.filter('formatNumber', function (value) {
            return numeral(value).format('0,0.[000]')
        })

        Vue.component('input-number', {
            template: '\
    <div>\
      <label v-if="label">{{ label }}</label>\
      <input\
        ref="input"\
        v-bind:value="displayValue(value)"\
        v-on:input="updateValue($event.target.value)"\
        v-on:focus="selectAll"\
      >\
    </div>\
  ',
            props: {
                value: {
                },
                label: {
                }
            },
            methods: {
                updateValue: function (inputValue) {

                    var valToSend = inputValue

                    var numVal = numeral(valToSend).value()
                    
                    if (isNaN(numVal)) {
                        valToSend = this.value.toString()
                        numVal = numeral(valToSend).value()
                    }

                    var formattedVal = numeral(numVal).format('0,0.[000]')

                    this.$refs.input.value = formattedVal + (valToSend.endsWith('.') ? '.' : '')

                    this.$emit('input', numeral(formattedVal).value())
                    

                },
                displayValue: function (inputValue) {
                    return numeral(inputValue).format('0,0.[000]')
                },
                selectAll: function (event) {
                    // Workaround for Safari bug
                    // http://stackoverflow.com/questions/1269722/selecting-text-on-focus-using-jquery-not-working-in-safari-and-chrome
                    setTimeout(function () {
                        event.target.select()
                    }, 0)
                }
            }
        })
        var app = new Vue({
            el: '#app',
            data: {
                pricePerGram: '160000',
                weightInGrams: '1.5',
            },
            computed: {
                totalPrice: function () {
                    return (this.pricePerGram * this.weightInGrams)
                },
                toatlWithVat: function () {
                    return (this.totalPrice *1.09)
                }
            }
        })
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/numeral.js/2.0.6/numeral.min.js"></script>

    <div id="app">
        <input-number v-model="pricePerGram" label="Price per gram:"></input-number><br />
        <input-number v-model="weightInGrams" label="Weight in grams:"></input-number><br />
        <div><label>Total price: </label><span dir="ltr">{{totalPrice | formatNumber}}</span></div><br />
        <div><label>Total + Vat: </label><span dir="ltr">{{toatlWithVat | formatNumber}}</span></div><br />
    </div>

Am I doing it right? Is there a better way to implement a numeric only input?

I'm looking to improve this. Using Numeral.js is not mandatory. This is just a library I found.

a few things regarding current implementation:

  • Supports thousand separator (as you type).

  • Supports decimal point with 3 digits (I like to improve this and accept unlimited number of decimal digits. it is because of this format 0,0.[000]. I couldn't find any format accepting unlimited decimal digits with Numeral.js)

  • only accepts numbers, comma & point (no character allowed).

    I can also use regular expression instead of Numeral.js. Can this be an improvement?

Community
  • 1
  • 1
Afshin Gh
  • 7,918
  • 2
  • 26
  • 43

1 Answers1

1

This should work (Codepen):

Vue.component("input-number", {
  template: '<input type="string" v-model="model">',

  props: {
    value: {
      type: String,
      required: true,
    }
  },

  computed: {
    model: {
      get() {
        // We extract decimal number, beacause toLocaleString will automagically
        // remove the dot and zeros after it while the user is still typing
        let value = this.value.split(".");
        let decimal = typeof value[1] !== "undefined"
          ? "." + value[1]
          : "";

        return Number(value[0]).toLocaleString("en-GB") + decimal;
      },

      set(newValue) {
        this.$emit("input", newValue.replace(/,/g, ""));
      }
    }
  }
});

new Vue({
  el: "#app",
  data: {
    input: "1234567.890",
  }
});
Filip Sobol
  • 5,198
  • 3
  • 13
  • 24
  • Your code looks really clean and promising. I may replace "toLocaleString" with regular expression but i think your approach is cleaner than mine. thank you mate. – Afshin Gh Apr 08 '18 at 21:17