Ito Pizarro
Ito Pizarro

Reputation: 1607

Vue.js (v. 1.0.14) Vue Filter versus Vue Validator

I have a form, on which I am trying to use Vue's "currency" filter on certain inputs that are then validated using Vue Validator (https://github.com/vuejs/vue-validator).

HTML

<validator name="foo">
  <input id="example" type="tel" v-model="baz | currency" v-validate:bar="['required']" />...
  <span> equals {{ baz }}</span>...
</validator>

JavaScript

Vue.config.warnExpressionErrors = false;

var vm = new Vue({
    el: '#demo',
    data: {
    baz: ''
    }
});

The filtered and validated fields update with every keystroke — such that they are cleared/reset each time. The effect is that attempting to key in a number, such as 1234, will result in the <input> showing "$3.004" or "$4.00" (though you may see: "$1.00" "$1.002" "$2.00" "$2.003" or "$3.00" as you type).

I'm thinking there's a conflict between the filter and the component for which gets the final say over the value(?)

There's a very good possibility that I'm not implementing this correctly. I distilled the issue down to the salient components int he following JSFiddle…

http://jsfiddle.net/itopizarro/b9a2oyL4/

Upvotes: 3

Views: 1453

Answers (1)

Peter
Peter

Reputation: 12711

I think the main problem has to do with the currency filter. Every time there's a key event in your input, the following things occur:

  1. the value of the model gets updated to the value of the input
  2. the updated model is read, filtered through the currency filter and displayed in the input (and your cursor gets put at the end)

When you type 1-2-3, it goes like this:

1 =>

  • baz = 1
  • input displays $1.00

2 =>

  • baz = "$1.002"
  • input displays "" (because the currency filter can't parse "$1.002")

3 =>

  • baz = 3
  • input displays "$3.00"

Part of the problem is that the built-in currency filter is a one-way filter - it formats the model for display but doesn't do anything when data is written back. You could try writing your own two-way currency filter. Here's a sample:

Vue.filter('currencyInput', {
  // model -> view
  read: function(value) {
    // use the built-in currency filter for display
    var currencyFilter = Vue.filter('currency');
    return currencyFilter(value);
  },
  // view -> model
  write: function(value, oldValue) {
    var number = +value.replace(/[^\d.]/g, '')
    return isNaN(number) ? 0 : parseFloat(number.toFixed(2))
  }
})

This ensures that the model is displayed as currency but written/stored as a number. This is still not perfect though because every time you keyup, the data gets formatted and the cursor moves to the end.

You could use the debounce or lazy attributes on the input so the update doesn't occur until the user has paused or moved on from the field.

<input id="example" type="tel" v-model="baz | currencyInput" debounce="500" v-validate:baz="['required']" />

But then you don't get the immediate formatting as the user types.

I guess it depends on your requirements. Hopefully this gives you some ideas.

Upvotes: 2

Related Questions