When working with lists, it's common to find yourself with raw data. Sometimes, you need to get this data filtered so that it can be read by the user. To do this, we need a combination of computed properties to form a final set of filters and sorters.
In this recipe, we will learn how to create a simple filter and sorter that will control our initial to-do task list.
Getting ready
The prerequisite for this recipe is Node.js 12+.
The Node.js global objects that are required for this recipe are as follows:
- @vue/cli
- @vue/cli-service-global
We can continue with our to-do list project or create a new Vue project with the Vue CLI, as we learned in the Creating your first project with Vue CLI recipe.
How to do it...
Follow these steps to add a set of filters and sorts to your list:
- In the App.vue file, at the <script> part, we will add new computed properties; these will be for sorting and filtering. We will add three new computed properties: baseList, filteredList, and sortedList. The baseList property will be our first manipulation. We will add an id property to the task list via Array.map. Since JavaScript arrays start at zero, we will add 1 to the index of the array. The filteredList property will filter the baseList property and return just the unfinished tasks, while the sortedList property will sort the filteredList property so that the last added id property will be the first that's displayed to the user:
<script>
import CurrentTime from "./components/CurrentTime.vue";
import TaskInput from "./components/TaskInput";
export default {
name: "TodoApp",
components: {
CurrentTime,
TaskInput
},
data: () => ({
taskList: [],
}),
computed: {
baseList() {
return [...this.taskList]
.map((t, index) => ({
...t,
id: index + 1
}));
},
filteredList() {
return [...this.baseList]
.filter(t => !t.finishedAt);
},
sortedList() {
return [...this.filteredList]
.sort((a, b) => b.id - a.id);
},
displayList() {
return this.sortedList;
}
},
methods: {
formatDate(value) {
if (!value) return "";
if (typeof value !== "number") return value;
const browserLocale =
navigator.languages && navigator.languages.length
? navigator.languages[0]
: navigator.language;
const intlDateTime = new Intl.DateTimeFormat(browserLocale, {
year: "numeric",
month: "numeric",
day: "numeric",
hour: "numeric",
minute: "numeric"
});
return intlDateTime.format(new Date(value));
},
addNewTask(task) {
this.taskList.push({
task,
createdAt: Date.now(),
finishedAt: undefined
});
},
changeStatus(taskIndex) {
const task = this.taskList[taskIndex];
if (task.finishedAt) {
task.finishedAt = undefined;
} else {
task.finishedAt = Date.now();
}
}
}
};
</script>
- For the <template> part, we will add Task ID and change how the changeStatus method sends the argument. Because the index is now mutable, we can't use it as a variable; it's just a temporary index on the array. We need to use the task id:
<template>
<div id="app">
<current-time class="col-4" />
<task-input class="col-6" @add-task="addNewTask" />
<div class="col-12">
<div class="cardBox">
<div class="container">
<h2>My Tasks</h2>
<ul class="taskList">
<li
v-for="(taskItem, index) in displayList"
:key="`${index}_${Math.random()}`"
>
<input type="checkbox"
:checked="!!taskItem.finishedAt"
@input="changeStatus(taskItem.id)"
/>
#{{ taskItem.id }} - {{ taskItem.task }}
<span v-if="taskItem.finishedAt"> |
Done at:
{{ formatDate(taskItem.finishedAt) }}
</span>
</li>
</ul>
</div>
</div>
</div>
</div>
</template>
- We also need to update our function inside the changeStatus method. Since the index now starts at 1, we need to decrease the index of the array by one to get the real index of the element before we can update it:
changeStatus(taskId) {
const task = this.taskList[taskId - 1];
if (task.finishedAt) {
task.finishedAt = undefined;
} else {
task.finishedAt = Date.now();
}
}
- To run the server and see your component, you need to open a Terminal (macOS or Linux) or Command Prompt/PowerShell (Windows) and execute the following command:
> npm run serve
Here is the component rendered and running:
How it works...
The computed properties worked together as a cache for the list and made sure there were no side effects when it came to manipulating the elements:
- For the baseList property, we created a new array with the same tasks but added a new id property to the task.
- For the filteredList property, we took the baseList property and only returned the tasks that weren't finished.
- For the sortedList property, we sorted the tasks on the filteredList property by their ID, in descending order.
When all the manipulation was done, the displayList property returned the result of the data that was manipulated.
See also
- You can find more information about Array.prototype.map at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map.
- You can find more information about Array.prototype.filter at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter.
- You can find more information about Array.prototype.sort at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort.