Displaying currency is often in a format of 3.50
but as a number it is displayed as 3.5
.
An option to choose the amount of decimals in a v-text-field that has type="number"
would be nice here.
Property: decimals
as number
If you choose 3, then it would always display contents of the text field as 3.500
.
Perhaps it will display 3.5
while typing and 3.500
after blur. I imagine this would make the implementation alot simpler.
This would probably be an extension of the current masking system to allow arbitrary mask lengths,
I would like to see this implemented to support locales and different currency formats. Most (maybe all?) european countries use comma as decimal separator. A lot of european countries also use space as thousands separator, while others use dot.
It's not only about separators, it's also about grouping digits, some country use 3 digits in a group (1.234.567,00) and some use 2 (12.34.567,00)
Not sure how useful the following link is, but I post it anyways:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString
Seems to me that recent additions to javascript and new browsers aim to make the multi-locale-currency-formatting-hell a thing of the past.
@valpet Although it is supported at a certain extent, it's purpose is mainly for display. Using it in an input
field is still a programming-hell thing because as-you-type editing is not internally supported. Given a myriad of international character sets, it's overwhelmingly challenging to detect what's considered separators, what's considered numerically acceptable, and how to control the cursor position when you do character inserts and deletions.
Here's an example:
This input is set to display locales='ar-EG'
and options={ style: 'currency', currency: 'AED' }
.
The moment I entered '1' it showed the surrounding prefix and suffix, then when I entered '4' a separator got inserted but the cursor was behind the '4'.
Until a feature like this is implemented by Vuetify natively, if you're using Vuetify ร la carte, you could extend VTextField in a custom FormattedInput.js component with something like:
import {VTextField} from 'vuetify'
export default {
name: 'formatted-input',
extends: VTextField,
props: {
blurredFormat: {
type: Function,
default: (v) => {
return v
}
}
},
methods: {
showValue () {
if (!this.isFocused) {
this.lazyValue = this.blurredFormat(this.inputValue)
} else {
this.lazyValue = this.inputValue
}
}
},
watch: {
isFocused () {
this.showValue()
}
},
mounted () {
this.showValue()
}
}
Then import and use your new
There's probably a better way to do this, but it's what I'm currently doing until something better comes along.
If there is an interest, I'm willing to modify AutoNumeric as needed to make it work with v-text-field
.
What would be the way to go about integrating it with that component?
@AlexandreBonneau that would be awesome as there currently isn't any working currency support for Vuetify. The v-money directive comes close, but it won't update when the model changes. There was a PR to introduce dynamic masks (and cover numeric / currency masking) but this has been closed. I think integration with AutoNumeric would be really useful
@plasmid87 well, if you only want a vue component that integrates AutoNumeric, you can directly use the official vue-autoNumeric component (and it support v-model updates as well as other external changes ;)).
However since my goal is to have an input that behaves like Vuetify's v-text-field
, I've whipped up a quick and dirty hack by 'vuetifying' vue-autonumeric
, by creating a ~monster~ component named v-autonumeric
๐ฑ.
It works for what I need, but a real integration where the v-text-field
could use an external library like AutoNumeric is really the only way to go there, because, let me repeat it again; it's pretty ugly now :)
If you want to play with it, have fun with that temporary component.
Did I say it's a hack? ๐
@AlexandreBonneau hey this is a great hack, thank you for sharing it! ๐๐ I agree with you that directives on v-text-field
that are compatible with AutoNumeric would be ideal and solve this issue. The trouble with using another component instead is that we can't easily use things like the built-in validation hooks, styling, hints, etc. without maintaining this sibling component just for localized numerics and currency.
It might be possible to extend v-text-field
, override genInput
and inject vue-autonumeric
... there might be a more elegant way of doing this, but I'll take a look
For an Excel like number input, I use the following pattern:
Template
<v-text-field
v-model="amount"
@blur="focus.amount = false"
@focus="focus.amount = true"/>
Script
data() {
return {
focus: {
amount: false
},
amount_: '0'
}
},
computed: {
amount: {
get() {
return mask(this.amount_, 2, this.focus.amount)
},
set(value) {
this.amount_ = unmask(value)
}
}
}
Helper
export const isDecimal = function(value) {
return /^-?(?:0|0\.\d*|[1-9]\d*\.?\d*)$/.test(value)
}
export const unmask = function(value, ds = ',') {
return value.replace(ds, '.')
}
export const mask = function(value, dp = -1, editing = false, ds = ',', gs = '.') {
if (editing || !isDecimal(value)) {
return value.replace('.', ds)
}
const parts = value.split(/\./)
let dec = parts[1] || ''
if (dp >= 0) {
dec = dec.length < dp ? dec.padEnd(dp, '0') : dec.substr(0, dp)
}
if (dec) {
dec = ds + dec
}
return parts[0].replace(/(\d)(?=(?:\d{3})+$)/g, '$1' + gs) + dec
}
It is worth looking at https://github.com/text-mask/text-mask for integration with vuetify. Masking is better than what vueitfy currently has and has number formatter as well.
Apparently text-mask converted from directive to component on 2017/02/28:
https://github.com/text-mask/text-mask/pull/360
https://github.com/text-mask/text-mask/issues/511
I'm new to Vue/Vuetify: I take it this would complicate integrating text-mask in to Vuetify?
Well in the last 6 months I've come across my own issue 4 times... Too bad nothing has been done with it yet.
I get that there are a ton of possible options to add here, but I think the biggest problem for currency here is that there is no (easy) way to change the decimal and thousands separators.
For finances that is simply a must-have and complementing that with the prefix option that already exists, it is good enough in most scenarios.
Browsers already support Number.toLocaleString(), why not simply add support for that? Add a method or emit for the raw value and it's simple but effective.
Well, on my end I tried integrating AutoNumeric with Vuetify, but with the ever-changing refactoring and mainly the fact that I don't know much about the Vuetify code organization, I unfortunately did not advance much.
If a Vuetify dev is willing to team up (and do a coding session with shared screens for instance), I'm up for the task :)
Feel free to try out my implemention here:
https://github.com/paulpv/vuetify-number-field
I ran in to complications making it a standalone Node package, so for now you would just have to copy it's code directly in to the Vuetify's code's components folder.
I just wrote a simple component that does what I need. Feel free to use it.
In my case it is specifically for Dutch formatting but that can be easily changed.
Basically it displays 10000,10
and 10000,1
as 10.000,10
and reverts back to the former on focus. Should handle the comma, minus and NaN. Hopefully this helps someone :)
Beautiful @Christilut!
Just updated it for some minor fixes. And if you notice a bug, feel free to let me know (or fix :nerd_face: )
Gist it!
@Christilut I trying to adjust this to pt-BR (BRL), but don't have success.
It's nice if anyone implements this directly to Vuetify ๐
I'll try to make it a bit more generic. But I can't promise it will work
with everything, too many possible options out there
On Thu, Jun 7, 2018, 03:26 Everton Nogueira notifications@github.com
wrote:
I trying to adjust this to pt-BR (BRL), but don't have success.
It's nice if anyone implements this directly to Vuetify ๐โ
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/vuetifyjs/vuetify/issues/2138#issuecomment-395263467,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ADT4A2XdU59LAepHDcg0_V37s2hyiJB3ks5t6IFfgaJpZM4P1V1O
.
You can find the gist here
If you find any bugs, report them there and I'll try to fix them and update the gist. I've tested โฌ and $ which seem to work. If it feels robust enough I'll turn it into a package :)
@ehvetsi try the gist version. If it doesn't work, let me know what values you're using and what you expect to see and I'll check it out.
Thanks, it's working to me! ๐
Any updates? I also need a currency mask, and would love to have it without workarounds.
I haven't had time to follow the latest Vuetify improvements, so I'm not sure if implementing AutoNumeric into a VTextField is easier now, but I'm still willing to integrate it with Vuetify, given a dev guidance!
Can't you just change the step
attribute?
<v-text-field
v-model="height"
min="0"
step=".1"
type="number"
></v-text-field>
@rvboris this is great!
What is this this.$refs.field.$refs.input
trickery? :)
@rvboris this is great!
What is this
this.$refs.field.$refs.input
trickery? :)
No tricks, just access to child ref of component
https://vuejs.org/v2/guide/components-edge-cases.html#Accessing-Child-Component-Instances-amp-Child-Elements
consider using https://sarahdayan.github.io/dinero.js in a similar way moment.js is used to format time?
@waspinator it looks like dinero.js is a library to format a given amount/currency/locale, but does not manage any live user input like a v-text-field
component do (and like AutoNumeric does).
I'm not sure this would add the needed user interaction management to Vuetify.
masks have been removed in 2.0
masks have been removed in 2.0
I'm terrified with this. I had many issues doing the decimal mask, the mask that came with vuetify was just perfect for the other cases. I think this is a great lost for the project. Vuetify is awesome, I love you guys but we need to talk when the subject is removal of features, maybe deprecate and remove in a next version?
Unfortunately, below solution is not working as expected. After you reset value by clicking Reset Button
, if you hover text field, you see the field is updated to old value. Sorry.
Any solution would be appreciated.
~I was searching a solution, tried a simple workaround with autonumeric
by installing autonumeric
and adding it directly to DOM element. I wanted to share here. It seems OK currently, but I didn't test it throughly. Please post if you disregard:~
~I added a rule, counter and model access to test quickly as below:~
$ npm install autonumeric
<template>
<div>
<v-text-field ref="priceField" v-model="price" label="Price" counter="10" :rules="rules"></v-text-field>
<button @click="price = 0">Reset Button</button>
</div>
</template>
<script>
import AutoNumeric from "autonumeric";
export default {
data: function() {
return {
price: 0,
rules: [v => v.length <= 7 || "Max 7 characters"];
};
},
mounted: function() {
const vuetifyField = this.$refs.priceField; // Please note `ref="priceField"` in `v-text-field`.
const inputField = vuetifyField.$refs.input; // Raw DOM Element.
const e = new AutoNumeric(inputField); // Add autonumeric to DOM field.
}
};
</script>
I just made a very hacky but working VNumericField
based on AutoNumeric
and VTextField
. v-model is working even if set multiple times later after mounting. The v-model is the raw value of AutoNumeric (getNumber()
)
// VNumericField.vue extending VTextField
<script>
import { VTextField } from "vuetify/lib";
import AutoNumeric from "autonumeric";
export default {
extends: VTextField,
props: {
anOptions: { // autonumeric options (see doc)
type: Object,
required: false,
default() {
return {};
}
}
},
data() {
return {
anElement: null, // autonumeric instance
changedByInput: false // Flag to know if the value is changed by user input
};
},
mounted() {
// Create the AutoNumeric instance on the VTextField input element
this.anElement = new AutoNumeric(this.$refs.input, this.anOptions);
// Set the AutoNumeric default value
this.anElement.set(this.value);
},
methods: {
onInput() {
// User has changed the input
this.changedByInput = true; // set the flag to true
this.updateVModel(); // emit v-model
},
updateVModel() { // emit raw value
if (this.anElement !== null) {
this.$emit("input", this.anElement.getNumber());
}
},
genInput() {
const listeners = Object.assign({}, this.listeners$);
delete listeners["change"];
let element = this.$createElement("input", {
style: {},
attrs: {
...this.attrs$,
autofocus: this.autofocus,
disabled: this.disabled,
id: this.computedId,
placeholder: this.placeholder,
readonly: this.readonly,
type: this.type
},
on: {
blur: this.onBlur,
input: this.onInput,
focus: this.onFocus,
keydown: this.onKeyDown,
"autoNumeric:formatted": () => {
this.changedByInput = false; // Remove the flag when autonumeric finish formatting
}
},
ref: "input"
});
return element;
}
},
watch: {
value(newVal) {
// Check if the last v-model update is fired by the input
if (!this.changedByInput) {
this.anElement.set(this.value); // Set the AutoNumeric raw value
}
}
}
};
</script>
BEWARE: DOES NOT WORK
Unfortunately, below solution is not working as expected. After you reset value by clicking
Reset Button
, if you hover text field, you see the field is updated to old value. Sorry.Any solution would be appreciated.
~I was searching a solution, tried a simple workaround with
autonumeric
by installingautonumeric
and adding it directly to DOM element. I wanted to share here. It seems OK currently, but I didn't test it throughly. Please post if you disregard:~~I added a rule, counter and model access to test quickly as below:~
$ npm install autonumeric
<template> <div> <v-text-field ref="priceField" v-model="price" label="Price" counter="10" :rules="rules"></v-text-field> <button @click="price = 0">Reset Button</button> </div> </template> <script> import AutoNumeric from "autonumeric"; export default { data: function() { return { price: 0, rules: [v => v.length <= 7 || "Max 7 characters"]; }; }, mounted: function() { const vuetifyField = this.$refs.priceField; // Please note `ref="priceField"` in `v-text-field`. const inputField = vuetifyField.$refs.input; // Raw DOM Element. const e = new AutoNumeric(inputField); // Add autonumeric to DOM field. } }; </script>
there is a problem when the object is inside a dialog or v-if="false", how do you solve that??
I just made a very hacky but working
VNumericField
based onAutoNumeric
andVTextField
. v-model is working even if set multiple times later after mounting. The v-model is the raw value of AutoNumeric (getNumber()
)// VNumericField.vue extending VTextField <script> import { VTextField } from "vuetify/lib"; import AutoNumeric from "autonumeric"; export default { extends: VTextField, props: { anOptions: { // autonumeric options (see doc) type: Object, required: false, default() { return {}; } } }, data() { return { anElement: null, // autonumeric instance changedByInput: false // Flag to know if the value is changed by user input }; }, mounted() { // Create the AutoNumeric instance on the VTextField input element this.anElement = new AutoNumeric(this.$refs.input, this.anOptions); // Set the AutoNumeric default value this.anElement.set(this.value); }, methods: { onInput() { // User has changed the input this.changedByInput = true; // set the flag to true this.updateVModel(); // emit v-model }, updateVModel() { // emit raw value if (this.anElement !== null) { this.$emit("input", this.anElement.getNumber()); } }, genInput() { const listeners = Object.assign({}, this.listeners$); delete listeners["change"]; let element = this.$createElement("input", { style: {}, attrs: { ...this.attrs$, autofocus: this.autofocus, disabled: this.disabled, id: this.computedId, placeholder: this.placeholder, readonly: this.readonly, type: this.type }, on: { blur: this.onBlur, input: this.onInput, focus: this.onFocus, keydown: this.onKeyDown, "autoNumeric:formatted": () => { this.changedByInput = false; // Remove the flag when autonumeric finish formatting } }, ref: "input" }); return element; } }, watch: { value(newVal) { // Check if the last v-model update is fired by the input if (!this.changedByInput) { this.anElement.set(this.value); // Set the AutoNumeric raw value } } } }; </script>
try to set v-model to zero, and it doesnt work
finally this solution works for me https://phiny1.github.io/v-currency-field/config.html#component-props
Modified @Webifi's code above. Working example here: https://codesandbox.io/s/vuetify-money-input-ixd66
// money-text-field.js
import { VTextField } from "vuetify/lib";
var formatter = new Intl.NumberFormat("en-IE", {
style: "currency",
currency: "EUR"
});
export default {
name: "money-text-field",
extends: VTextField,
data: () => ({
inputValue: null
}),
props: {
blurredFormat: {
type: Function,
default: (v) => {
// Format and remove the currency symbol - use prefix="โฌ" attribute for cleaner look
if (v) return formatter.format(parseFloat(v)).slice(1);
}
}
},
methods: {
showValue() {
if (!this.isFocused) {
// Store the value before change
this.inputValue = this.lazyValue;
this.lazyValue = this.blurredFormat(this.lazyValue);
} else {
// Show unformatted value on focus
this.lazyValue = this.inputValue;
}
}
},
watch: {
isFocused() {
this.showValue();
},
value() {
// Handle v-model updates
if (!this.isFocused) {
this.showValue();
}
}
},
mounted() {
this.showValue();
}
};
Most helpful comment
If there is an interest, I'm willing to modify AutoNumeric as needed to make it work with
v-text-field
.What would be the way to go about integrating it with that component?