-
Run
yarn && yarn serve
inside this folder. -
In your IDE, open the
src
folder and explore the code. -
Have you noticed how sometimes the total price in the shopping cart looks like
CHF 115.05000000000001
?In javascript all numbers are stored as floating point numbers and the arithmetic is known to not always be accurate.
It would be great if we could instruct Vue.js to treat some numbers as currencies, round and format them correspondingly.
The actual formatting is easily done with the Internationalization API:
new Intl.NumberFormat('en-us', { style: 'currency', currency: 'CHF' }).format(123.456789); // will result in CHF 123.45
-
Let's create a global
price
filter, that does exactly that and use it every component, that displays prices.Hint
main.js
import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store' Vue.config.productionTip = false Vue.filter('price', (value) => { return new Intl.NumberFormat('en-us', { style: 'currency', currency: 'CHF' }).format(value); }) new Vue({ router, store, render: h => h(App) }).$mount('#app')
Cocktail.vue
<template> .... <button v-if="ingredient.price" @click="orderIngredient(ingredient)" :class="$style.button"> Buy for {{ ingredient.price | price }} </button> .... </template>
ShoppingCart.vue
<template> .... <p> Subtotal: CHF {{ subtotal | price }}<br /> Delivery fee: CHF {{ deliveryFee | price }}<br /> Total: CHF {{ total | price }} </p> .... </template>
ShoppingCartItem.vue
<template> .... <div> <strong>{{ title }}</strong> <br /> {{ price | price }} <br /> Qty: {{ quantity }} </div> .... </template>
-
Now let's imagine our shop becomes available in different countries and has to display prices in different currencies. We will not be able to hardcode
CHF
or theen-us
locale in the filter anymore. We can store the currency value in the Vuex store and pass it to the filter as a parameter. Let's adjust just theCocktail
component so far.Hint
store.js
.... export default new Vuex.Store({ state: { error: undefined, allRecipes: [], shoppingCartItems: {}, favoriteCocktails: [], currency: 'CHF', locale: 'en-us' }, .... })
main.js
.... Vue.filter('price', (value, locale, currency) => { return new Intl.NumberFormat(locale, { style: 'currency', currency }).format(value); }) ....
Cocktail.vue
<template> .... <button v-if="ingredient.price" @click="orderIngredient(ingredient)" :class="$style.button"> Buy for {{ ingredient.price | price(locale, currency) }} </button> .... </template> <script> .... export default { .... computed: { currency() { return this.$store.state.currency; }, locale() { return this.$store.state.locale; } }, .... }; </script>
-
An error happens now when we open Shopping Cart - we are not passing
locale
andcurrency
parameters to our price filter there. In order not to re-create computed properties in every component we need, let's:- declare a
localizationMixin
mixin in a separate file - import it into those components, that use
price
filter - make sure we pass
locale
andcurrency
parameters everywhere we useprice
filter
Hint
localizationMixin.js
export default { computed: { currency() { return this.$store.state.currency; }, locale() { return this.$store.state.locale; } } }
Cocktail.vue
(and other components usingprice
filter)<script> .... import localizationMixin from '@/localizationMixin'; export default { mixins: [localizationMixin], .... }; </script>
- declare a
-
Now let's go to the
store.js
and switch locale to 'de-DE' and currency to 'EUR' to verify that our logic works. The price formatting should change fromCHF 38.35
to38,35 €
.
lab12
Folders and files
Name | Name | Last commit date | ||
---|---|---|---|---|
parent directory.. | ||||