Stack Overflow is a community of 4.7 million programmers, just like you, helping each other.

Join them; it only takes a minute:

Sign up
Join the Stack Overflow community to:
  1. Ask programming questions
  2. Answer and help your peers
  3. Get recognized for your expertise

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/

share|improve this question
up vote 0 down vote accepted

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.

share|improve this answer
1  
Fantastic, thank you! The "lazy" attribute does what I was looking for. jsfiddle.net/itopizarro/b9a2oyL4/7 The filter alone left the input in the same position (once it does the .toFixed(), the cursor is on the wrong side of the string). jsfiddle.net/itopizarro/b9a2oyL4/6 – Ito Pizarro Jan 18 at 13:46

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.