Vue.js: Input formatting using computed property is not applying when typing quick

Viewed 418

I am trying to format data in this format m:ss, and limit the input textbox to only show 3 or fewer characters. (m = minute, s = seconds)

I have the below code, it formats properly in m:ss format. But if I type many characters at once, it shows more than 3 characters or it gets stuck.

I.E) If I type 11111 at once, I get 1:1111. But I want the textbox to show 1:11.

If I type 1234, sometimes it gets stuck at 1:2. Then If I wait and type again, there are more than 3 characters.

new Vue({
  el:"#app",
  data () { return { value: "" } },
  computed: {
    fValue: {
      // getter
      get () {
        if (this.value.length > 3) { return this.value.substr(0, 3); }
        return this.value;
      },
      // setter
      set (newValue) {
        this.formatTime(newValue);
      }
    }
  },
  methods: {
    formatTime (str) {
      let totalLength = str.length;
      if (totalLength > 3) { totalLength = 3; }
      let a = str.replace(/[^0-9]/g, "").substr(0, 1);
      let b = str.replace(/[^0-9]/g, "").substr(1, 1);
      if (b > 5) { b = 5; }
      const c = str.replace(/[^0-9]/g, "").substr(2, 1);
      if (totalLength >= 2) { a = `${a.substring(0, 1)}:${b}${c}`; }
      const result = a;
      this.value = result;
      return result;
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <input
    v-model="fValue"
    id="format-value"
    class="input"
    type="text"
  />
</div>

------ EDIT Question 2 -----

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
   <div
      v-for="(index) in valueInputs" <-- index
      :key="index"
    >
      <input
        v-model="value"     // <-- I want to track what index I'm in
        @input="formatTime" // <-- so I can set it in an array later
        maxLength="4"       // I tried formatTime[index] or value[index]
        id="format-value"
        class="input"
        type="text"
      />
  </div>
</div>

data () {
  return {
  valueInputs: [],    // a list of inputs
  allFormatValues: [] // want to store all the formatted values here by the index
 }
}

Want to set an array that stores all the formatted values:

   this.allFormatValues[index] = this.value;

I'm not sure how to associate the index with the formatted string value?

1 Answers

You can use value as the v-model and call formatTime on input. Here is a demo with some refactoring/improvements:

new Vue({
  el:"#app",
  data () { return { value: "" } },
  methods: {
    formatTime () {
      const numericValue = this.value.replace(/[^0-9]/g, "");
      let [a = '', b = '', c = ''] = numericValue.substr(0, 3).split('');
      b = b > 5 ? 5 : b;
      this.value = numericValue.length >= 2 ? `${a}:${b}${c}` : a;
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <input
    v-model="value"
    @input="formatTime"
    maxLength="4"
    id="format-value"
    class="input"
    type="text"
  />
</div>

Related