This content originally appeared on DEV Community and was authored by Sabin Adams ???
Filtering lists is a super basic, but very important piece to almost any application. VueJS provides a nice property called computed
that allows us to reactively update our UIs as data in the model changes. Let's take a look at this in action by building a searchable list of Pokémon!
The complete project can be found on github
Basic Setup
The code for this project was put together using the Vue CLI and all of the PokéData is from the PokéAPI. I've gone ahead and added some components and styling to make the final product a bit more interactive and visually appealing. We'll just focus on the filtering mechanism for now.
The main file we will be playing with here is App.vue
, a basic Vue component that display's the Pokémon logo and a list of Pokémon. It imports a custom composition function
I've created to handle grabbing our Pokémon list (plus some extra functionality for the final product). It also uses a PokéBall component to display each Pokémon.
<template>
<div class="container">
<img alt="Pokemon logo" src="./assets/pokemon-logo.svg" />
<div class="pokemon-list">
<PokeBall v-for="(poke, i) in pokemon" balltype="poke" :pokemon="poke" :key="i"/>
</div>
</div>
</template>
<script>
import { usePokemonList } from '@/composable/usePokemonList'
import PokeBall from '@/components/pokeball'
export default {
name: "App",
setup() {
const { pokemon } = usePokemonList()
return {
pokemon
}
},
components: {
PokeBall
}
}
</script>
<style scoped>
@import 'app.css';
</style>
Handling Field Input
The goal here is to be able to filter down our list by name to make it easier to find what you are looking for.
NOTE: We are only grabbing 100 Pokés in this example. The final product on github has the ability to load more, but we won't worry about that in this tutorial
To do this, we'll first need some sort of UI to handle the input value we want to filter our list by. Let's add an input field <template>
to handle this:
<template>
<div class="container">
<img alt="Pokemon logo" src="./assets/pokemon-logo.svg" />
// Shiny new search bar with an input field
<div class="search-bar">
<input
class="search-box"
placeholder="Filter pokémon..."
/>
</div>
<div class="pokemon-list">
<PokeBall v-for="(poke, i) in pokemon" balltype="poke" :pokemon="poke" :key="i"/>
</div>
</div>
</template>
Great! We have a nice-looking search bar, but it doesn't really do anything yet... We need to allow Vue access to the field's value in a reactive way so that we always know what the current value is and can filter our list accordingly. To do this, we'll need to create a property we'll call filterText
and bind that value to the input field. Here's how:
In our <template>
, we will add a v-model
attribute to our input field with a value of the property name we want to bind the field's value to.
<input
class="search-box"
placeholder="Filter pokémon..."
v-model="filterText"
/>
In our <script>
section we need to create the filterText
property and expose it to the template. This value needs to be reactive (to update automatically as the input field changes), so we will need to use Vue's ref
property.
import { ref } from 'vue'
import { usePokemonList } from '@/composable/usePokemonList'
import PokeBall from '@/components/pokeball'
export default {
name: "App",
setup() {
const { pokemon } = usePokemonList()
const filterText = ref('')
return {
pokemon,
filterText
}
},
components: {
PokeBall
}
}
Here, we've imported the ref
property from Vue and created a new ref called filterText
in our setup function. In order for this to be available to the rest of the Vue component (template, computed properties, etc...) we need to add it to the setup()
's return object.
I've gone ahead and added some output above to demonstrate the reactivity of the new
filterText
property
Now that we've got our input taken care of, let's put it to use!
Filtering Our List
Vue has a handly little property called computed
that suits our needs perfectly. computed
values are functions that return a value that can be bound to the template just like a variable. The function is aware of its dependencies and their values, so as values change in the Vue component, if a value is a dependency of the computed variable the template will be updated in a reactive way.
What does that mean? It's a bit confusing without seeing it in action. Let's apply this to our Pokémon list:
import { ref, computed } from 'vue'
import { usePokemonList } from '@/composable/usePokemonList'
import PokeBall from '@/components/pokeball'
export default {
name: "App",
setup() {
const { pokemon } = usePokemonList()
const filterText = ref('')
const filteredPokemon = computed( () => {
let filter = filterText.value
if (!filter.length) return pokemon.value
return pokemon.value.filter( poke =>
poke.name.toLowerCase().includes(filter.toLowerCase())
)
})
return {
pokemon,
filterText,
filteredPokemon
}
},
components: {
PokeBall
}
}
Here we are creating a new "computed" property of the Vue component named filteredPokemon
. This property has two dependencies, filterText
and pokemon
because these are both properties of the Vue component as well. As those properties change, this filteredPokemon
property will update itself using the logic provided in the function and all of the bindings in the template will reflect the changes.
So in this case, as we type into the input field, causing an update to filterText
, the filteredPokemon
property will update using the new filter string.
NOTE: To access the values of our
ref
s within the setup function, we need to access their.value
properties. This isn't necessary within the<template>
To do the actual filter, we are using Javascript's Array method .filter()
. This will go through each item in an array and run a function on each item. If the function returns true, it will keep the item, otherwise it will leave it out. The .filter()
function returns a new array with the filtered results.
With this computed property completed, we can now bind our list to the filteredPokemon
property instead of the pokemon
property so that we only see the filtered results instead of the whole array.
<div class="pokemon-list">
<!--- Note we are now binding to filteredPokemon --->
<PokeBall v-for="(poke, i) in filteredPokemon" balltype="poke" :pokemon="poke" :key="i"/>
</div>
Now as we type into the input box, it should filter our results.
Conclusion
Vue's computed
property is a powerful tool that makes our job of reactive data-binding infinitely easier. Anything from generating lists, to transforming data, to applying classes/styling to elements on the page as properties change is possible with this single property, along with so many other uses.
The sample we built is just a basic list with some filtering, but I've added some other cool functionality in the completed project. If you'd like to take a look at this thing built out a bit more, check out the github link provided at the top of the page.
Thanks for reading, I hope it was the very best, like no tutorial ever was ?
... Okay, that was a bit cheesy, but ?
Go catch 'em all!
P.S. If you liked this article, be sure to follow me on Twitter to get updates on new articles I write!
This content originally appeared on DEV Community and was authored by Sabin Adams ???
Sabin Adams ??? | Sciencx (2021-05-13T05:25:02+00:00) Vue 3 List Filtering: Pokémon Edition. Retrieved from https://www.scien.cx/2021/05/13/vue-3-list-filtering-pokemon-edition/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.