MoneyReady
Money is a composable that converts monetary values from integers to various monetary string formats and vice versa, ensuring proper formatting for both display and JSON:API interactions.
<script setup lang="ts">
const numberValue = ref('12345.6789')
const moneyInstance = computed(() => {
const value = Number(numberValue.value)
return Number.isFinite(value) ? useMoney(value) : undefined
})
const formatNumberValue = (value?: number | string) =>
Number.isFinite(Number.parseFloat(String(value))) ? value : '-'
const data = computed(() => [
{
title: 'Minor unit',
value: moneyInstance.value?.minor,
},
{
title: 'Major unit',
value: moneyInstance.value?.major,
},
{
title: 'Formatted (default)',
value: moneyInstance.value?.toFormat(),
},
{
title: 'Formatted (numberFull)',
value: moneyInstance.value?.toFormat('numberFull'),
},
{
title: 'Formatted (number2dps)',
value: moneyInstance.value?.toFormat('number2dps'),
},
{
title: 'Formatted (number4dps)',
value: moneyInstance.value?.toFormat('number4dps'),
},
{
title: 'Formatted (currencyFull)',
value: moneyInstance.value?.toFormat('currencyFull'),
},
{
title: 'Formatted (currency2dps)',
value: moneyInstance.value?.toFormat('currency2dps'),
},
{
title: 'Formatted (currency4dps)',
value: moneyInstance.value?.toFormat('currency4dps'),
},
])
// Watcher to only allow typing numbers in JavaScript's `Number` format with . as decimal separator
watch(numberValue, (newValue, oldValue) => {
// Count the number of decimal points in the new value
const decimalPointCount = (newValue.match(/\./g) || []).length
// If there are more than one decimal point, revert to old value
if (decimalPointCount > 1) {
numberValue.value = oldValue
return // Early return to avoid unnecessary processing
}
// Remove any non-numeric characters except for decimal points
numberValue.value = newValue.replace(/[^0-9.]/g, '')
})
</script>
<template>
<provet-card padding="l">
<h4 slot="header">Money formatting</h4>
<provet-stack
direction="horizontal"
justify-content="space-between"
class="n-margin-be-l"
>
<provet-input
v-model="numberValue"
disallow-pattern="[^0-9.]"
type="unit"
class="n-margin-be-l"
label="Conversion from major unit"
:style="`--n-input-text-align: start`"
>
<div slot="start">major</div>
</provet-input>
</provet-stack>
<provet-table>
<table>
<thead>
<tr>
<th>Conversion</th>
<th :style="`text-align: right`">Result</th>
</tr>
</thead>
<tbody>
<tr v-for="({ title, value }, key) in data" :key>
<td>{{ title }}</td>
<td :style="`text-align: right`">
{{ formatNumberValue(value) }}
</td>
</tr>
</tbody>
</table>
</provet-table>
</provet-card>
</template>
Usage
This section includes guidelines for designers and developers about the usage of this pattern in different contexts.
Do
- Use the composable to handle the conversion of monetary values between major and minor units, ensuring proper formatting for display and JSON:API interactions.
- Leverage the example implementation to allow users to input monetary values in the
major
format and preview various formatted results. - Use computed properties to dynamically create a money instance and provide preformatted values like
minor
,major
, and various formatting options such ascurrency2dps
andnumber4dps
. - Add input validation to restrict users to entering only valid numeric formats with a single decimal point for accurate conversion.
- Use the
toFormat
method to display formatted monetary values in a table or UI component, ensuring clarity for the user. - Provide intuitive labels and use helpers like
formatNumberValue
to display fallback values (e.g.,'-'
) for invalid or undefined inputs.
Don't
- Don't bypass the composable's formatting methods (
toFormat
) to create custom formats, as this can lead to inconsistencies with other monetary displays. - Avoid allowing users to enter invalid numeric strings with multiple decimal points or non-numeric characters; implement input validation as shown in the example.
- Don't rely solely on raw
minor
ormajor
values for display purposes; always use formatting for a user-friendly experience. - Avoid using this composable in scenarios where unsupported decimal precision or exotic currency formats are required.
- Don't hardcode output formatting or values in the UI; ensure values are dynamically computed and formatted using the provided methods.
- Avoid using the composable outside of monetary-related contexts, as it is designed specifically for handling monetary units and formatting.
Additional considerations
- JSON:API will handle monetary values as integers with 2 or 4 decimal places and expects to receive them in the same format. However, on the frontend, we need to format these values in various ways. For example, values in input fields are strings. Therefore, we need to convert integers from JSON:API into monetary monetary strings.
- If this input is part of a form submission, we must convert that string back into an integer for the JSON:API POST request.
Integration
This product pattern is currently only available to use in the New Frontend for Provet Cloud (using Vue & Nuxt).
Troubleshooting
If you experience any issues while using this pattern, please ask for support in the #vet-frontend Slack channel.