Scroll down to the script below, click on any sentence (including terminal blocks!) to jump to that spot in the video!
One of the things that we need to know on every page load is what the current
category is. To get that, we pass it from the server to Vue by
currentCategoryId global variable. To make that
a bit nicer, we even created a
page-context service that reads
the global variable for us. We use this info to highlight which category
is active on the sidebar and also to filter the products.
One thing that we could have done is just call the
function in every component that needed that info. For example,
sidebar.vue, where we need the current category, we could have
page-context service and called
we could have done the same thing in
catalog. That would be safe
currentCategoryId is not something that changes: it's not
one of those things where, when it changes, we need our component to
re-render. We don't need it to be reactive.
But now, I do want to do this. Here's our new mission: make it possible to change
currentCategoryId while our app is running and for the sidebar and products to
instantly update. This now means that we do need
currentCategoryId to be
"reactive". And that means it needs to live as
data in a component.
Look at the component tree in the Vue dev tools. You know the drill: if both
currentCategoryId, then it needs to live as
<Products>. Then, we can pass it down via
By the way, in Vue 3, it is possible to have reactive objects outside of a Vue component.
The good news is: we planned ahead! Look inside of
products.vue. Hey! This has a
currentCategoryId computed property, which
calls - I'll hold command or control and click - the
Then, we pass this down to the other components via a prop.
This means that all we need to do is change
currentCategoryId from a computed
data and... everything should just work! What could go wrong?
Remove this computed property and,
data, add up a new variable called
currentCategoryId. I'll set its
initial value to
getCurrentCategoryId(). We can still use the service to
get the initial value.
If we go to the browser, everything still works wonderfully! It's
still passing down
currentCategoryId in exactly the same way. The cool
thing now is, I can go the Vue Dev tools, click on
<Products>, scroll down
currentCategoryId and change it. Let's, say
24 and... boom! Our
app updated! The new category is highlighted in the sidebar and we can see
the correct title. Victory!
Oh... but it did not change the list of products!
Let's try this again: I'll change to
25 - your id numbers will probably
be different - and... again! It updated the
title... but not
the product list. What's going on?
Put on your debugging hat and dive into
catalog.vue, the component that
products data depends on two things. If we
go down to
loadProducts() - the method that actually makes the AJAX call -
we can see that
products depends on the
searchTerm and also on
currentCategoryId. This means that when either of these change,
we need to re-call
loadProducts() so that it will make the new AJAX request.
Making sure that
loadProducts() was called when the
was easy because the
search-bar component already emits an
event whenever the search changes. We then call the
this.loadProducts() and that makes the AJAX call and changes
products data. That part is wonderful.
The question now is: how can we run code when
How can we detect when a prop changes?
Here's the answer: When there is no other event or hook that you can listen
to and you simple must execute code when a prop or data changes, you can use
something called a
watcher! Very simply: a watcher is a function that's called
by Vue whenever a specific prop or data changes.
I'm not gonna lie. Watchers kind of get a bad reputation. But there's nothing
wrong with them. The problem is that, a lot of times, people use watchers
when there's actually a better solution available. For example, look
search-bar. Remember: we're using
v-model in the template to bind our
input to the
searchTerm data. I also added
@input so that, down here, we
could emit the custom event.
Another way that we could have done this is, instead of listening with
we could have added a watcher for
searchTerm: a function that is called whenever
that data changes. Then, whenever
searchTerm changes and our watcher function
was called, we would emit the custom event.
The reason I didn't do that is, as I just said, watchers are kind of your
last resort. They're not as performant as other parts of your system. In this
case, we did have another option: listening via
But looking back in
catalog, there's simply no other solution. We need to run
code when the
currentCategoryId prop changes. And we can't create a computed
products because the code we need to run is async. That is why
we're going to use a watcher.
Let's do it next!