I am trying to add a formatted field to my vue app by using maska. Until now it works fine if my value do not have faction digits (for instance 26767).
But if I get a value with faction like 26717.01 i get the output 2.671.701 (i expect 26.717,01)
Can someone point me to the right direction?
<script setup lang="ts">
import { vMaska } from "maska/vue";
const emit = defineEmits([ 'update:modelValue']);
let props = defineProps({
amount: {
type: Number
},
})
const options = {
number:{
fraction: 2,
locale: 'de-DE'
},
};
</script>
<script lang="ts">
import {defineComponent} from "vue";
export default defineComponent({
name: 'AmountField',
});
</script>
<template>
<v-text-field
variant="outlined"
:clearable=false
density="compact"
auto-grow
rows="1"
:model-value="props.amount"
v-maska="options"
v-bind="$attrs"
@maska="(event) => {console.log(event.detail.unmasked); emit('update:modelValue',Number(event?.detail?.unmasked))}"
/>
</template>
<style scoped>
</style>
my testcases looks like:
import AmountField from "@/components/elements/AmountField.vue";
describe('xxxx', (): void => {
it('no fraction', () => {
cy.mount(AmountField, {
propsData: {
amount: 26717,
editable: true,
id: "amountField"
}
} as any).then(({wrapper, component}) =>
{
cy.get("#amountField").find('input').should('have.value', '26.717').should('not.be.disabled')
cy.get("#amountField").find('input').type('{selectAll}1234,56').then(() => {
expect((wrapper).emitted('update:modelValue')).to.have.length;
expect((wrapper).emitted('update:modelValue')[7][0]).to.equal(1234.56)
})
});
})
it('with fraction', () => {
cy.mount(AmountField, {
propsData: {
betrag: 26717.01,
editable: true,
disabled: true,
id: "amountField"
}
} as any);
cy.get("#amountField").find('input').should('have.value', '26.717,01')
})
})
This is an issue in Maska (v3.0) with numeric input and a format that uses a dot as grouping separator (like the german 1.000.000,99
). Even when input is not masked, Maska starts by removing the grouping separator. So when input is a float and grouping separator is a dot, the decimal point will be removed. 26717.01
becomes 2671701
, which is formatted as 2.671.701
.
I assume this is not intended behavior. As a workaround, you can start with an already formatted value:
<template>
<v-text-field
v-model="maskedValue"
v-maska:amount.unmasked="options"
/>
</template>
<script setup>
const amount = ref(26717.01)
const options = {...}
const preformattedValue = new Intl.NumberFormat(options.number.locale).format(amount.value)
const maskedValue = ref(preformattedValue)
</script>
If amount
can be changed from outside, you probably have to use a watcher to update the formatted value.
const { createApp, ref } = Vue;
const { createVuetify } = Vuetify
const vuetify = createVuetify()
const app = {
directives: {maska: window.Maska.vMaska},
template: `
<v-app>
<v-main>
<v-container>
<v-text-field
v-model="maskedValue"
v-maska:amount.unmasked="options"
/>
<pre>amount: {{amount}}</pre>
</v-container>
</v-main>
</v-app>
`,
setup(){
const options = {
number: {
fraction: 2,
locale: 'de',
},
reversed: true,
}
const formatter = new Intl.NumberFormat(options.number.locale)
const amount = ref(26717.01)
return {
amount,
maskedValue: ref(formatter.format(amount.value)),
options,
}
}
}
const a = createApp(app).use(vuetify).mount('#app')
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/vuetify@3/dist/vuetify.min.css" />
<link href="https://cdn.jsdelivr.net/npm/@mdi/font/css/materialdesignicons.min.css" rel="stylesheet">
<div id="app"></div>
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@3/dist/vuetify.min.js"></script>
<script src="https://unpkg.com/maska@3.0.0/dist/cdn/vue.js"></script>