Vue 3 Money Conversion App Composition API

The composition API is a new and optional way of creating and organizing components in VueJS 3. It is based on the functional programming principle called composition. In this tutorial, we will use it to create a basic money conversion app.

Setting up Vue Project with the Composition API

Setting up a VueJS 3 project is not hard at all, but since the 3rd version of the framework is not out yet, then we will be actually using VueJS 2 with a composition-API plugin. The plugin will provide us with the features that we will need.

First, we can set up a standard VueJS application using VueJS CLI, then we will install the composition-api plugin:

$> vue create vue-3-money-converter

And we can just go with the default configuration. Furthermore, we will install the plugin:

$> npm i -S @vue/composition-api

Finally, we will add it to Vue:

import Vue from 'vue'
import App from './App.vue'
import VueCompositionApi from '@vue/composition-api'

Vue.config.productionTip = false

Vue.use(VueCompositionApi)

new Vue({
  render: h => h(App)
}).$mount('#app')

Example of using Ref and Reactive

With the composition API, we now have a new method in our options object, called setup.

In the setup method, we create reactive data, computed values, and register lifecycle hooks. In the end, we will return an object containing all the data, method, and computed values we will need in the template.

This is useful, for example for creating non-reactive variables and using them in the template, think of enums.

To create reactive data, we can use the reactive function and theref function. The difference is that the Ref function will return an object that has a value property containing the value we passed initially.

If we want to change the value of the ref we have to mutate the value property. Or if it’s an object, we have to change one of its properties. On the other hand, reactive accepts only objects and returns the same initial value passed.

Deciding which function to use depends on what you’re trying to achieve. However, here is a resource explaining why we should stick to ref all the time. Let’s see them in action first:

<template>
  <div>
    <input v-model="form.username" />
   is submitted {{ isSubmitted }}
  </div>
</template>

<script>
import {
  ref,
  reactive
} from '@vue/composition-api'

export default {
  setup () {
    const isSubmitted = ref(false)
    const form = reactive({ username: '' })

    return {
      form,
      isSubmitted
    }
  }
}
</script>

Basically this is similar to creating an object and a boolean in the data methods. Now, let’s start building a small rates conversion app and explore the API further.

Fetching Conversion Rates with Axios

We will use Axios to fetch the exchange rates from the exchangerates.io API, First, let’s install Axios:

$> npm i -S axios

Next, we will create a new API folder and inside it an exchange-rates.js file. We will create an Axios instance pointed at the exchange rates API. Then, expose a method to fetch the data:

import { create as createAxiosInstance } from 'axios'

const exchangeRatesApi = createAxiosInstance({
  baseURL: 'https://api.exchangeratesapi.io'
})

// this will wait for the promise to resolve the response object and then return the data property
export const getLatest = async () => (await exchangeRatesApi.get('latest')).data

The create method from Axios allows us to create a new instance, this will be a unique instance with its own configuration. If you’re dealing with multiple endpoints this is a great way to separate them. We specified a base URL which is the exchange rates API. Then, when we want to make a request we just specify the endpoint.

Since Axios request returns an object containing the data, The getLatest method will return the data directly.

Converting Money using Reactivity

We will use now the composition API to call our API and display the rates.

First, in our main App.vue component. We will create the setup method. Inside it, we will do the request to fetch the exchange rates using our API:

<template>
  <div id="app"></div>
</template>

<script>
import {
  ref,
  onUnmounted,
  onBeforeMount
} from '@vue/composition-api'

import { getLatest } from '@/api/exchange-rates'

export default {
  setup () {
    let interval
    const exchangeRates = ref({})

    onBeforeMount(() => {
      interval = setInterval(async () => {
        exchangeRates.value = await getLatest()
      }, 1000)
    })

    onUnmounted(() => {
      if (interval) {
        clearInterval(interval)
      }
    })

    return {
      exchangeRates,
    }
  }
}
</script>

As you can see the beforeMount and beforeDestroy hooks have been replaced with methods onBeforeMount and onUnmounted. The trick here is to call these methods inside of the setup function so they can be registered in the component.

We created two variables, the interval variable is not reactive and we just use it to manage the interval that fetches the data periodically. We store the interval in a variable so that we clear it when the component is unmounted.

The other variable is reactive and it holds the result of our request. Since it’s a ref, we have to assign the data to its value property.

Next, since we have the exchange rates, we will create an input that will hold the amount we want to convert. We will also create a computed value that will hold the result of the conversion since the result is a result of a computation (amount * rate), a computed value is a perfect choice for this.

<template>
  <div id="app"></div>
</template>

<script>
import {
  ref,
  computed,
  onUnmounted,
  onBeforeMount
} from '@vue/composition-api'

import { getLatest } from '@/api/exchange-rates'

// a function that takes the amount and rates and returns an mapped object containing the result for each rate
const convertExchangeRates = (amount, exchangeRates) => {
  if (!exchangeRates.value.rates || !Object.keys(exchangeRates.value.rates).length) {
    return {}
  }

  return Object.keys(exchangeRates.value.rates)
    .reduce((rates, key) => {
      const rate = exchangeRates.value.rates[key] * amount.value

      rates[key] = rate.toFixed(2)

      return rates
    }, {})
}

export default {
  setup () {
    let interval
    const exchangeRates = ref({})
    // the amount is reactive since it will be used in a v-model
    const amount = ref(0)

    // the computed value calls the convertExchangeRates function
    const convertedRates = computed(() => convertExchangeRates(amount, exchangeRates))

    onBeforeMount(() => {
      // ...code
    })

    onUnmounted(() => {
      // ...code
    })

    return {
      exchangeRates,
      amount,
      convertedRates
    }
  }
}
</script>

Finally, we will display the data:

<template>
  <div>
    <input v-model.number="amount" />
    {{ exchangeRates.base }} <small>{{ exchangeRates.date }}</small>

    <ul>
      <li
        v-for="(rate, key) in convertedRates"
        :key="key"
      >
        {{ key }} : {{ rate }}
      </li>
    </ul>
  </div>
</template>

<script>
  // ...code
</script>

Our result should look something like this:

final result of money conversion app with the VueJS 3 composition API

Final Words

The new composition api brings more flexibity to how we create and use our Vue.js Component.

Further reading about the composition api in action: