In this article I'm going to share how to write vue3 component that convert currencies with calculation of user bonuses (it means if you give users promo codes or any other benefits).
Basic layout
First we need to make a sketch of our component template:
<template>
<div>
<input
:class="$style.input"
@input="valueHandler(($event.target as HTMLInputElement).value)"
@keypress="press" v-model="value"
>EUR
</div>
<div>You will receive with bonus</div>
<div>
<input
:class="$style.input"
@input="valueReceiveHandle(($event.target as HTMLInputElement).value)"
v-model="valueReceive"
@keypress="press"
>USD
</div>
</template>
<style module>
.input {
border: solid 2px #cdcdcd;
padding: 10px 15px;
margin: 10px;
font-size: 16px;
}
</style>
In input handler function I set event target value from template because I want to call this one in another component parts
Currency Conversion Logic
Ok, how it works when user enters some data.
When user enters data to the first input your function should to convert source value by currency rate and update value in second input. We need create two variables value and valueReceive and set as v-model directive value.
const value = ref(100)
const valueReceive = ref(0)
Then write the pure conversion functions:
const convert = (value: number) => {
return Number(value.toFixed(2))
}
type ConverterType = {
value: number;
rate: number;
}
const convertTo = ({ value, rate }: ConverterType) => {
return convert(value * rate)
}
const convertFrom = ({ value, rate }: ConverterType) => {
return convert(value / rate)
}
Component handlers
After that let's define component props rate and modelValue:
const props = withDefaults(
defineProps<
Partial<{
rate: number;
}>
& {
modelValue: number;
}
>()
,
{
rate: 1,
}
)
Define handle functions for inputs:
const valueHandler = (value: number | string) => {
valueReceive.value = convertTo({ value: Number(value), rate: props.rate })
}
const valueReceiveHandle = (_value: number | string) => {
value.value = convertFrom({
value: Number(_value),
rate: props.rate
})
}
It looks good, but let's add watcher for modelValue prop. It will call valueHandler and emit for update modelValue, and don't forget to set immediate: true
, otherwise it wont fire when the component is mounted or component takes on newest value.
const emits = defineEmits<{
'update:modelValue': [value: number]
}>()
watch(() => props.modelValue, (_value: number) => {
emits('update:modelValue', _value)
value.value = _value
valueHandler(_value)
}, {
immediate: true
})
So this works, but how about handling a user's keypress, because if a user can enter any symbols, this is not expected behavior for the component - accept only numbers and dot:
const press = (e: KeyboardEvent) => {
const onlyDigitsAndDots = /^[0-9]*\.?[0-9]*$/
if (!onlyDigitsAndDots.test(e.key)) e.preventDefault()
}
User bonuses
After writing the main logic, let's imagine, customer (your project manager for example) comes to us and says: "I want to the app gives users the opportunity to apply bonuses and make calculations". We say: "Ok, it's easy"
We need to write two functions: calculateBonus (will calculate bonus in receive input) and subtractBonus (will subtract the bonus from final value and set it to value input), and bonusPercent prop.
type BonusType = {
value: number;
percent: number;
}
const calculateBonus = ({value, percent}: BonusType) => {
return value + (value * (percent / 100))
}
const subtractBonus = ({ value, percent }: BonusType) => {
return value / (1 + percent / 100)
}
Let's refactor our handle functions:
const valueReceiveHandle = (_value: number | string) => {
value.value = convertFrom({
value: subtractBonus({value: Number(_value), percent: props.bonusPercent}),
rate: props.rate
})
}
const valueHandler = (value: number | string) => {
const _value = props.bonusPercent ? calculateBonus({
value: Number(value), percent: props.bonusPercent
}) : Number(value)
valueReceive.value = convertTo({ value: _value, rate: props.rate })
}
Use it
How to use converter component:
<template>
<CurrencyConverter
v-model="value"
:rate="1.08"
:bonus-percent="27"
/>
</template>
<script setup lang="ts">
import CurrencyConverter from './components/CurrencyConverter.vue'
import { ref } from 'vue';
const value = ref(100)
</script>
Top comments (0)