Scroll down to the script below, click on any sentence (including terminal blocks!) to jump to that spot in the video!
We've already been organizing our code in several ways. The biggest way is that we've been breaking our components down into smaller pieces, which is awesome! We also created services for fetching data, whether that's via AJAX calls or by grabbing a global variable.
product-card.vue. We created a computed property that
takes the product's price, divides it by a hundred and then converts it into
decimal digits to display in the template.
Having logic inside your components is a bit like having logic inside of controllers in Symfony. It's not the worst thing ever, but it makes your controllers harder to read. It also means that you can't reuse that code from other parts of your app or unit test it.
But... I'm not going to put this logic into the
services/ directory because,
at least in this project, I'm using
services/ to mean "things that fetch data".
In functional programming, a
helper is a term that's often used for functions that
take input, process it and return something else. And this is exactly
what our new function will do.
So, inside of
js/, create a new directory called
helpers/ and then a new
format-price.js. In here,
export default and, actually, let's
use the more hipster arrow syntax to say that I
export default a function.
For the body of that function, go to
product-card, copy the formatting code
and... paste. But change to use the
Brilliant! Now, you might notice that ESLint is angry. Does it NOT like hipster code? How dare you, ESLint? Oh, no! Phew...! It says
Unexpected block statement surrounding arrow body. Move the return value immediately after the arrow.
My ESLint rules are set up so that if I have an arrow function that only has
one line, and that one line a return statement, we should add parentheses around
the statement then remove the
return and the semi-colon at the end.
That's now an implied return.
If you don't like that, just use the normal function syntax. And, to earn the admiration of our teammates, let's add some JSDoc: the price is a number and let's even describe what the function does.
And... woo! We now have a nice reusable function! Oh, and there are two ways to
First, you can have a file, like
default a single function. Or, if you have several different
helper functions related to pricing or number manipulations, you could create a
file called, maybe,
number.js and then export named functions. That second
idea is what we're doing inside of
services/. It's up to you to decide which
you like better.
Ok! Let's go use this inside of
The first thing we need to do is import it into the
component. Do that with
import formatPrice from '@/helpers/format-price'
Now, when I first started using Vue, I thought:
Hey! I now have a local variable called
formatPricein this file! So let's go right up to the template and use it!
product.priceto reference the
productobject and its
But... it was not to be! If you move over to the browser's console... our dreams are crushed! It says:
Property or method
formatPriceis not defined on the instance, but referenced during render.
Of course! When you reference a variable or function in a template
we know that what it really does is call
this.formatPrice(). It does not
try to find some local
formatPrice variable. On our instance, we do have a
computed property called
price but no methods.
So... we can't just import
formatPrice and expect it
code, like in our computed property.
Change the template code back to
price. Now, in the computed method,
use the new helper:
return formatPrice(), and pass the same
thing we did before:
This time, when we move over... yes! It works perfectly! Let me get rid of the search term and... nice! No errors in the console.
Next: Let's make our search bar a little bit fancier by adding an X icon on the right to clear the search!