The last piece of functionality we miss is the ability to filter bills by category.
We want to click a category name in the NavBar component, and the table and chart should only display that category’s bills.
And we’ll add an ‘All’ button that is the default and will let us go back to show all the bills instead of filtering by one category.
Let’s do it!
I’m going to first restructure the App
component a little bit, by:
- adding an
activeCategory
property to the state - creating an
activeBills
computed property that will only return the bills associated with the currently selected categories (or all if none is selected) - passing
activeBills
to the BillsTable and Chart components, instead ofbills
so they will show the filtered bills.
The first change is easy:
export default {
name: 'app',
data() {
return {
bills: [],
categories: [],
shouldShowAddCategory: false,
shouldShowAddBill: false,
activeCategory: '',
}
},
//...
}
Let’s create the activeBills
computed property. We filter the bills by activeCategory
, and we sort them by date in reverse chronological order (newest first):
export default {
name: 'app',
//...
computed: {
activeBills() {
return this.bills
.filter((bill) =>
this.activeCategory ? bill.category === this.activeCategory : true
)
.sort((a, b) => (new Date(a.date) < new Date(b.date) ? 1 : -1))
},
},
}
Now we pass that to the child components, instead of bills
:
<div class="w-1/2 bg-gray-200">
<BillsTable
:bills="activeBills"
v-on:triggerShowAddBill="triggerShowAddBill"
v-on:removeBill="removeBill"
/>
</div>
<div class="w-1/2 bg-gray-400 pt-4 pl-4 text-2xl">
<Chart :bills="activeBills" />
</div>
Now we’re ready to go in the NavBar component and do our edits there. We’ll get back to the App component later to handle a few events we’ll pass it.
Here’s the NavBar component at the moment, before any change:
<template>
<ul class="list-reset inline flex justify-center border-b-4 mb-0">
<li
class="p-4 inline bg-gray-200 hover:bg-gray-400 uppercase font-black cursor-pointer"
v-for="category in categories"
:key="category"
>
{{category}}
</li>
<li
class="p-4 inline bg-gray-200 hover:bg-gray-400 uppercase font-black cursor-pointer"
@click="triggerShowAddCategory"
>
➕
</li>
</ul>
</template>
<script>
export default {
props: ['categories'],
methods: {
triggerShowAddCategory() {
this.$emit('triggerShowAddCategory')
},
},
}
</script>
First thing, let’s accept another prop, activeCategory
:
props: ['categories', 'activeCategory'],
We’ll use that in the v-for
loop to apply the class bg-gray-600
if a category is active. This class is part of Tailwind and will apply a darker background. We use :class
because it’s dynamic. Vue will merge class
and :class
for us.
Also, when we click a category we call the setActiveCategory
method, passing the category name:
<li
class="p-4 inline hover:bg-gray-400 uppercase font-black cursor-pointer"
:class="[ activeCategory === category ? 'bg-gray-600' : 'bg-gray-200' ]"
v-for="category in categories"
:key="category"
@click="setActiveCategory(category)"
>
{{category}}
</li>
Before iterating on categories
, let’s add an “All” button:
<li
class="p-4 inline hover:bg-gray-400 uppercase font-black cursor-pointer"
:class="[ activeCategory === '' ? 'bg-gray-600' : 'bg-gray-200' ]"
@click="clearActiveCategory()"
>
All
</li>
when this is clicked, it calls the clearActiveCategory
method.
Let’s implement those 2 methods we defined:
methods: {
//...
clearActiveCategory() {
this.$emit('clearActiveCategory')
},
setActiveCategory(category) {
this.$emit('setActiveCategory', category)
}
}
as you can see, we let the parent component handle them, App.
So let’s get back to the App component, and let’s add a v-on
directive for those events:
<NavBar
:categories="categories"
:activeCategory="activeCategory"
v-on:clearActiveCategory="clearActiveCategory"
v-on:setActiveCategory="setActiveCategory"
v-on:triggerShowAddCategory="triggerShowAddCategory"
/>
and let’s implement them:
clearActiveCategory() {
this.activeCategory = ''
},
setActiveCategory(category) {
this.activeCategory = category
}
Now whenever we click a category, we ask App to update the activeCategory state property, and that’s automatically passed down again to the NavBar component to let it highlight the category, and Vue is also updating the activeBills
computed property so our BillsTable and Chart components only show the active category amounts.
See the app in the current state on CodeSandbox.