If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeI've gotta say, now that we have dynamic products, these hardcoded categories are starting to stress me out! So let's make those dynamic too!
If you look at /api
, just like how we have a /api/products
endpoint for the collection of products, we also have one for /api/categories
. We can use that! And now that we know the correct way to make Ajax calls to load data, this "should" be simple! Famous last words!
Open sidebar.vue
. We created a data
property earlier called categories
, but... it's just hardcoded. Set this to an empty array to start.
... lines 1 - 42 | |
<script> | |
export default { | |
... lines 45 - 51 | |
data() { | |
return { | |
categories: [], | |
}; | |
}, | |
}; | |
</script> | |
... lines 59 - 73 |
To make the Ajax call, head over to catalog.vue
to celebrate one of programming's oldest arts: stealing code. Copy the entire created()
function and paste it in sidebar.vue
under data
. We just need to change the URL to /api/categories
and the data from this.products
to this.categories
.
... lines 1 - 42 | |
<script> | |
import axios from 'axios'; | |
... line 45 | |
export default { | |
... lines 47 - 58 | |
async created() { | |
const response = await axios.get('/api/categories'); | |
this.categories = response.data['hydra:member']; | |
}, | |
}; | |
</script> | |
... lines 66 - 80 |
Awesome! Let's... just see what happens! Refresh... whoa! I think it worked! No errors in the console... and in the dev tools, if you click on Sidebar
, we have real categories
data!
The data for each category is pretty simple: it has the normal @id
and @type
that comes from JSON-LD and it also has id
and name
.
Oh, and one thing I forgot to mention: when we pasted the created()
function, PhpStorm automatically added the axios
import for us. Sweet! Oh, but it used double quotes... which ESLint does not like. Let's fix this. Much better! If you want, you can tweak your PhpStorm settings to use single quotes automatically.
Anyways, head up to the v-for
directive. We learned earlier that every v-for
element must have a key
attribute. Until now, since our categories were hardcoded, we used the array index
for the :key
. But now we can be smarter because we know that each category has a unique @id
property.
Let's simplify the v-for
- we don't need index
anymore - and then say :key="category['@id']"
.
<template> | |
... lines 2 - 7 | |
<ul class="nav flex-column mb4"> | |
... lines 9 - 15 | |
<li | |
v-for="category in categories" | |
:key="category['@id']" | |
... line 19 | |
> | |
... lines 21 - 27 | |
</ul> | |
... lines 29 - 40 | |
</template> | |
... lines 42 - 80 |
The last thing that doesn't work is... our links! We originally set the href
to category.link
... but there is no link
property on the real category data.
Here's the plan: we will eventually create a separate page that will display all of the products for a specific category - the URL will be /categories/
and then the id
of the category. We'll worry about making sure that page exists later, but let's get the links working now.
If you're a Symfony user, then you're used to generating a URL to a route. But from JavaScript, I'm going to keep it simple and hardcode the URL to this new page. I think it's totally ok to do this: it keeps your JavaScript simpler. The tradeoff, of course, is that if you ever changed a URL, you would need to update your JavaScript.
Ok: what we want to do here is say /category/
and then print category.id
. But since we have the colon in front of href
to make this dynamic, we would need to have single quotes around the string, a plus sign and then category.id
.
That would work: in the browser, when I hover over categories, the right URL is display at the bottom of my browser.
But yikes! This code hurts to look at! Can we make it nicer? Of course! Replace the quotes with ticks
. Now, if we need dynamic code, write ${}
- so ${category.id}
.
... lines 1 - 7 | |
<ul class="nav flex-column mb4"> | |
... lines 9 - 15 | |
<li | |
... lines 17 - 19 | |
> | |
<a | |
:href="`/category/${category.id}`" | |
class="nav-link" | |
> | |
... line 25 | |
</a> | |
</li> | |
</ul> | |
... lines 29 - 80 |
This is a superpower of modern JavaScript, not Vue. Look over now and... yes! All of the links look perfect!
This is working nicely! We have dynamic products and dynamic categories. The only thing that bothers me is all the loading! Notice that each time we refresh, I see the products and categories loading!
When we only loaded the products, that was probably okay: it was just one spot that loaded pretty fast. But also having the categories waiting to load is starting to look a bit jarring. Plus, we're eventually going to have multiple pages that will use the same categories sidebar. This means that on every page, the user will wait for the same list of categories to be fetched via Ajax. We can do better!
So next, let's investigate how we can get data from the server in a way that avoids an Ajax call. We'll use this at first to highlight the current category on the sidebar. Then later, we'll replace the Ajax call completely.
// package.json
{
"devDependencies": {
"@symfony/webpack-encore": "^0.30.0", // 0.30.2
"axios": "^0.19.2", // 0.19.2
"bootstrap": "^4.4.1", // 4.5.0
"core-js": "^3.0.0", // 3.6.5
"eslint": "^6.7.2", // 6.8.0
"eslint-config-airbnb-base": "^14.0.0", // 14.1.0
"eslint-plugin-import": "^2.19.1", // 2.20.2
"eslint-plugin-vue": "^6.0.1", // 6.2.2
"regenerator-runtime": "^0.13.2", // 0.13.5
"sass": "^1.29.0", // 1.29.0
"sass-loader": "^8.0.0", // 8.0.2
"vue": "^2.6.11", // 2.6.11
"vue-loader": "^15.9.1", // 15.9.2
"vue-template-compiler": "^2.6.11", // 2.6.11
"webpack-notifier": "^1.6.0" // 1.8.0
}
}