In this tutorial, we'll explore basic click event filtering on a rendered list using JSON data in Vue.js. This is a basic tutorial, but it should be a solid foundation to start building something more complex.
TL;DR
Here is an example of a working CodeSandbox
https://codesandbox.io/s/kkvr7z5rr3
Why would we need to do this?
Filtering data is a part of my everyday life as a front-end dev. I'm often presented with a large piece of data that needs to be looped through, filtered, and displayed. In this tutorial, we will fetch()
a JSON file, build a rendered list from the data, and filter the list with click event controls.
We will not be using any dependencies outside of Vue.js.
How do we do this?
First, we'll need to have Vue.js up and running. This is covered here, or you can set up a CodeSandbox.
If you used the Vue CLI or CodeSandbox to set up your app, you'll likely already have the initial structure of your project in place. In case you don't, you'll need a place to display our future component.
// App.vue file
<template>
<div id="app">
</div>
</template>
<script>
export default {
name: "App"
}
</script>
The above code is a good starting point for this tutorial. In the provided CodeSandbox example, this file is named App.vue
. The code shown here will serve as the foundation for displaying the component we will build.
In this tutorial, we will be placing the filter controls and rendered list inside the same component. If this were a larger application or a longer tutorial, I would likely split them in two and share data between. I've written a separate tutorial on sharing data between multiple components here.
Okay, let's start building out our component.
In the provided example the file we are working with is named DataDisplay.vue.
// DataDisplay.vue file
<template>
<div>
</div>
</template>
<script>
export default {
name: "DataDisplay"
};
</script>
The first thing we'll work on is data. I've created sample JSON data through a service called JSON Generator. To bring the data into our DataDisplay.vue
file, we'll fetch()
the data in the created
lifecycle hook. Information on lifecycle hooks can be found here. All of the data returned will be stored in the data property users
.
// DataDisplay.vue file
<template>
<div>
</div>
</template>
<script>
export default {
name: "DataDisplay"
data: function() {
return {
users: []
}
},
created() {
var apiURL = "https://next.json-generator.com/api/json/get/4JCnNiTCr";
fetch(apiURL)
.then(res => res.json())
.then(res => (this.users = res))
.catch(error => console.log(error));
}
}
</script>
Now that we have our data stored, we can work on displaying it.
Let's loop through the data stored in the users
property with Vue's v-for
directive. The v-for directive requires a syntax structure of x in y
or (x, i) in y
. You can also use of
as the delimiter instead of in
. In this example, our syntax is "(entry, index) in users"
, where users
is the data source, entry
is an alias for the element being iterated on, and index
is the index of the item in the rendered list.
The test data provided in the example is a list of developers with some associated information for each. We'll render their name and main coding language in an unordered list. You can view the full JSON file here.
// DataDisplay.vue file
<template>
<div>
<ul class="userWrap">
<li
v-for="(entry, index) in users"
:item="entry"
:key="index"
class="user"
>
<h2>{{ entry.name }}</h2>
<span>
Primary Language: <strong>{{ entry.mainLanguage }}</strong>
</span>
</li>
</ul>
</div>
</template>
<script>
export default {
name: "DataDisplay"
data: function() {
return {
users: []
}
},
created() {
var apiURL = "https://next.json-generator.com/api/json/get/4JCnNiTCr";
fetch(apiURL)
.then(res => res.json())
.then(res => (this.users = res))
.catch(error => console.log(error));
}
}
</script>
You should see a list of developers and their main coding language. We can now build out our buttons to filter this list.
We will be adding a list of buttons, a series of data properties, and the v-if
directive to our results list.
Starting with the data properties. The fkey
property is the data field that we will be keying our filtering on. filterList
is an array of filter values we'll be checking our data against. The list of filter buttons will be built off filterList
as well. filter
contains the value of the current set filter. By default, we want All
of the users to show.
data: function() {
return {
fkey: "mainLanguage",
filterList: ["JavaScript", "Python", "PHP", "Java", "All"],
filter: "All",
users: []
}
}
Let's build out our filter control buttons.
We will use the v-for
directive again. This time, to iterate through the filterList
array and generate our filtering values. You'll notice two new pieces in our rendered list properties, @click
& :class
directives. The @click
will set the value for filter
when the button is clicked. :class
will set the button's class as active
when entry === filter
.
<button
v-for="(entry, index) in filterList"
:item="entry"
:key="index"
@click="filter = entry;"
:class="{ active: entry == filter }"
>
{{ entry }}
</button>
Next, we will connect our filtering buttons to our rendered user
list.
To do this, we will add Vue's v-if
directive to our list's properties. Our example uses v-if="entry[fkey] === filter || filter === 'All'"
. If our entry's mainLaguage
is equal to filter
or if filter
is set to 'All'
, it will return true
and show the entry.
<ul class="userWrap">
<li
v-for="(entry, index) in users"
v-if="entry[fkey] === filter || filter === 'All'"
:item="entry"
:key="index"
class="user"
>
<h2 class="title">{{ entry.name }}</h2>
<span class="language">
Primary Language: <strong>{{ entry.mainLanguage }}</strong>
</span>
</li>
</ul>
This is the full DataDisplay.vue
file. I added in some CSS for fun.
// DataDisplay.vue
<template>
<div>
<div>
<button
v-for="(entry, index) in filterList"
:item="entry"
:key="index"
@click="filter = entry; active = index;"
:class="{ active: entry == filter }"
>
{{ entry }}
</button>
</div>
<ul class="userWrap">
<li
v-for="(entry, index) in users"
v-if="entry[fkey] === filter || filter === 'All'"
:item="entry"
:key="index"
class="user"
>
<h2 class="title">{{ entry.name }}</h2>
<span class="language">
Primary Language: <strong>{{ entry.mainLanguage }}</strong>
</span>
</li>
</ul>
</div>
</template>
<script>
export default {
name: "DataDisplay",
data: function() {
return {
fkey: "mainLanguage",
filterList: ["JavaScript", "Python", "PHP", "Java", "All"],
filter: "All",
users: []
};
},
created() {
var apiURL = "https://next.json-generator.com/api/json/get/4JCnNiTCr";
fetch(apiURL)
.then(res => res.json())
.then(res => (this.users = res))
.catch(error => console.log(error));
}
};
</script>
<style scoped>
button {
background: #74b6cc;
border: none;
color: #fff;
padding: 10px;
margin: 5px;
}
button.active {
background: #0089ba;
}
.userWrap {
list-style-type: none;
padding: 2%;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
flex-direction: row;
}
.user {
padding: 10px;
margin: 1% 0;
border: 1px solid #ddd;
border-radius: 3px;
width: 45%;
text-align: left;
}
h2.title {
font-size: 1.3rem;
font-weight: bold;
margin: 0;
}
.language {
display: block;
font-size: 0.9rem;
}
</style>
The last step is to import our DataDisplay
component into our App.vue
file.
// App.vue
<template>
<div id="app">
<DataDisplay />
</div>
</template>
<script>
import DataDisplay from "./components/DataDisplay";
export default {
name: "App",
components: {
DataDisplay
}
};
</script>
🍻
Top comments (5)
I'm not sure but this also could be like this:
I used watch property.
I was expecting to read a similar implementation 😅
I guess separating all the filtering logic from markup feels more maintainable to me.
how to solve this ...
[Vue warn]: Property or method "enter" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: vuejs.org/v2/guide/reactivity.html....
found in
---> at src/components/DataDisplay.vue
at src/App.vue
Well written, thank you for sharing your knowledge, it is really helpful
Great tutorial! Any suggestions on adding multiple filter sets based on the json data?