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/[email protected]/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?

1 Answer 1

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",
  }
});
Sign up to request clarification or add additional context in comments.

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.