Build a Shopping Cart with Vue and Dinero.js
My friend Cory and I chat almost every day, so you can bet he knows about everything going on in my life. But as we were talking the other day, I realized he had no idea how Dinero.js, my latest project, actually works. Like, what you can do with it.
I paused and realized it may actually not be that obvious. It’s easier, whatever your skill level is, to understand what a smooth scrolling plugin does than what a money library has to offer.
“Do you see in JavaScript how you can use a Date constructor to store a date and format it later? Or you use Moment.js to create moment objects and how it’s better than storing dates as strings or any other type? Well, Dinero.js is like Moment, but for money. There’s no native way to handle money, and if you try to do it with
Number
types, you’re going to run into issues. That’s what Dinero.js helps you avoid. It secures your monetary values in objects and allows you to do whatever you need with them.”
I was happy with my explanation, as Cory started “a-ha”-ing. But I realized one thing had been missing from the beginning. Something that would speak volumes and help anyone understand the benefits of Dinero.js: a real-world example.
In this tutorial, we’ll build a shopping cart. We’ll use Vue.js to build the component, then integrate Dinero.js to handle all the money stuff.
TL;DR: this post goes in-depth in the how and why. It’s designed to help you grasp the core concepts of Dinero.js. If you want to understand the whole thought process, read on. Otherwise you can look at the final code on CodeSandbox.
This post assumes you have basic knowledge of Vue.js. If not, first check my tutorial “Build Your First Vue.js Component”. It will equip you with everything you need to go further.
Getting started
For this project, we’ll use vue-cli and the webpack-simple Vue.js template. If you don’t have vue-cli installed globally on your machine, fire up your terminal and type the following:
Then:
You can keep the default options for all questions. When it’s done, navigate to the new directory, install dependencies and run the project:
Webpack will start serving your project on port 8080
(if available) and open it in your browser.
Setting up the HTML/CSS
I won’t get into page structure and styling in this tutorial, so I invite you to copy/paste the code. Open the App.vue
file, and paste the following snippets.
This goes between the <template>
tags:
Ant this between the <style>
tags:
Adding data
When you’re dealing with products, you usually retrieve raw data from a database or an API. We can get close by representing it in a separate JSON file, then import it asynchronously as if we were querying an API.
Let’s create a products.json
file in assets/
and add the following:
This is pretty similar to what we would get from a real API: data as a collection, with titles and text as strings, and quantity and prices as numbers.
We can go back to App.vue
and set empty values in data
. This will allow the template to initialize while the actual data is being fetched.
Finally, we can fetch data from products.json
with an asynchronous request, and update the data
property when it’s ready:
Now let’s populate our template with this data:
You should see all the items in your cart. Now let’s add some computed properties to calculate the subtotal and total:
And add them to our template:
There we go! Try changing quantities around, you should see the subtotal and total amounts change accordingly.
Now we have a few issues here. First, we’re only showing amounts, not currencies. Sure, we could hard code them in the template right next to the reactive amounts. But what if we want to make a multi-lingual website? Not all languages format money the same way.
What if we want to show all amounts with two decimal places, for better alignment? You could try and keep all initial amounts as floats by using the toFixed
method, but then you’d be working with String
types which are a lot harder and less performant when it comes to doing maths. Also, that would mean changing data for purely presentational purposes, which never is a good idea. What if you need the same data for other purposes and it requires a different format?
Finally, the current solution is relying on floating point math, which is a bad idea when it comes to handling money. Try and change a few amounts:
Now, look at how broken your shopping cart is 😱 This isn’t some buggy JavaScript behavior but a limitation of how we can represent our decimal numbering system with binary machines. If you do math with floats, you’ll sooner or later encounter those inaccuracies.
The good news is, we don’t have to use floats to store money. That’s exactly where Dinero.js comes into play.
Dinero.js, a wrapper for money
Dinero.js is to money what Moment.js is to dates. It’s a library that lets you create monetary value objects, manipulate them, ask them questions, and format them. It relies on Martin Fowler’s money pattern and helps you solve all common problems caused by floats, primarily by storing amounts in minor currency unit, as integers.
Open up your terminal and install Dinero.js:
Then import it into App.vue
:
You can now create Dinero objects 🎉
Let’s create a factory method to turn our price
properties into Dinero objects on demand. We have floats with up to two decimal places. This means if we want to turn them into their equivalents in minor currency units (in our case, dollars), we need to multiply them by 10 to the power of 2.
We pass the factor
as an argument with a default value, so we can use the method with currencies that have different exponents.
Because we’re doing floating point math during the conversion, some calculations may end up as slightly inaccurate floats. That’s easy to fix by rounding the result to the closest integer.
Now we can use toPrice
in our computed properties:
And in our template:
If you look at your shopping cart, you’ll see {}
in place of prices. That’s because we’re trying to display an object. Instead, we need to format them so they can display prices with the right syntax, alongside their currency symbol.
We can achieve that with Dinero’s toFormat
method.
Look in your browser: you now have a well-formatted, fully functional shopping cart 🤗
Going further
Now that you have a good grasp of the basics of Dinero.js, time to raise the bar a little.
Presentation
Let’s change shippingPrice
to 0
in the JSON file. Your cart should now display “Shipping: $0.00”, which is accurate but not user-friendly. Wouldn’t it be nicer for it to say “Free”?
Fortunately, Dinero.js has a plenty of handy methods to ask questions to your instances. In our case, the isZero
method is exactly what we need.
In the template, you can display text instead of a formatted Dinero object whenever it represents zero:
Of course, you can generalize this behavior by wrapping it in a method. It would take a Dinero object as an argument and return a String
. This way, you could show “Free” whenever you try to display a zero amount.
Locale switching
Imagine you’re making an e-commerce website. You want to accommodate your international audience, so you translate content and add a language switcher. Yet, there’s one detail that may slip your attention: money formatting also changes depending on the language. For example, €10.00 in American English translates to 10,00 € in French.
Dinero.js supports international formatting via the I18n API. This lets you display amounts with localized formatting.
Dinero.js is immutable, so we can’t rely on changing Dinero.globalLocale
to reformat all existing instances. Instead, we need to use the setLocale
method.
First, we add a new property language
in data
and set it to a default value. For locales, you need to use a BCP 47 language tag such as en-US
.
Now we can use setLocale
directly on Dinero objects. When language
changes, the formatting will change as well.
Now we can add our language switcher:
When you click on the switcher, it will reassign language
, which will change how the objects are formatted. Because the library is immutable, this will return new objects instead of changing existing ones. It means if you create a Dinero object and decide to display it somewhere, then reference it somewhere else and apply a setLocale
on it, your initial instance won’t be affected. No pesky side effects!
All tax included
It’s common to see a tax line on shopping carts. You can add one with Dinero.js, using the percentage
method.
First, let’s add a vatRate
property in the JSON file:
And an initial value in data
:
Now we can use this value to calculate the total of our cart with tax. First, we need to create a getTaxAmount
computed property. We can then add it to getTotal
as well.
The shopping cart now shows the total with tax. We can also add a line to show what the tax amount is:
And we’re done! We’ve explored several concepts of Dinero.js, but that’s only scratching the surface of what it has to offer. You can read through the documentation and check out the project on GitHub. Star it, fork it, send me feedback, or even open a pull request! I have a nice little contributing guide to help you get started.
You can also look at the final code on CodeSandbox.
I’m currently working on bringing a convert
method to Dinero.js, as well as better support for all ISO 4217 currencies and cryptos. You can stay tuned by following me on Twitter.
Happy coding! 👩🏻💻