Although the “tasks” table has more columns, the very basic functionalities will be concerned with task names and due times. The API will return a bunch of names and times, and Vue will show them on the web page.
An important thing to note is that I put the call to the API to fetch tasks in the created life-cycle hook
this time. I used to do the initial API call in the mounted hook
. The official Vue documentation does it too. The difference is small, but the created hook is more logical for this, since fetching from the API doesn’t depend on whether the DOM can be changed or not.
created() {
this.fetchTasks(); // Vue instance not mounted to DOM yet
},
mounted() { // Vue instance has been mounted to DOM
this.fetchTasks();
}
I’m using Axios because it was already included. No idea how different it is from JavaScript’s native “fetch” or in which scenarios one is better than the other.
Since the API will return an array of tasks, a list of tasks can be made using v-for:
<ul>
<li v-for="task of tasks" :key="task.id">{{ task.name }}</li>
</ul>
This is a very rudimentary form. In our case, each task will be more complex than just some text inside an <li> element. So, it’s better to use single-file components
here.
That means the <li> elements can be replaced with a <task> component, with each task passed to the component:
<ul>
<task v-for="task of tasks" :key="task.id"
:task="task"
></task>
</ul>
In turn, the Task.vue file will have a task “prop” of type Object. The task name will be used as is, but a computed property will be used to show the time left (since leftTime only needs to update if dueTime changes):
<template>
<li>
<div>{{ task.name }}</div>
<div v-if="task.dueTime">{{ leftTime }}</div>
</li>
</template>
computed: {
leftTime() {
// Still haven't done the main part
return leftTime;
}
}
A v-if check is used for left time because it’s not mandatory for a task to have a due time.
To add some fields for editing the name and the due time is trickier though. We need three input fields: name, date, and time. (I think <input type="time"> has good support now. Need to check.) Plus two buttons to save and cancel. And another for removing the existing due time.
The <li> element is getting crowded now, because the task name and the “edit” button needs to show by default, and these should disappear and the edit fields and buttons should show when the “edit” button is clicked.
<template>
<li>
<template v-show="!isEditing">
<div>{{ task.name }}</div>
<div v-if="task.dueTime">{{ leftTime }}</div>
<button v-on:click="isEditing = true">Edit</button>
</template>
<template v-show="isEditing">
<!-- Input fields and buttons related to editing -->
</template>
</li>
</template>
computed: {
dueTimeParts() {
// Returns an object with two properties: due time's
// date part and time part.
// ("Due time" sounds confusing now, I know.)
}
},
methods: {
showEditSection() {
this.newName = this.task.name;
this.newDate = this.dueTimeParts.date;
this.newTime = this.dueTimeParts.time;
this.isEditing = true;
},
updateTask() {
//
}
}
(Using @ instead of v-on: would’ve been nice, but Hugo’s (or this theme’s, at least) syntax highlighter apparently doesn’t support Vue.)
I used v-show instead of v-if to toggle the task name and the edit section because tasks may be edited frequently. (I do it, though that’s more because of the current UI than anything else.) So, rendering at the beginning and toggling CSS display later
is fine with me here.
Now, clicking the “edit” button will reveal the edit section, which has three inputs fields bound to three properties via v-model attributes:
<template v-show="isEditing">
<input type="text" v-model="newName">
<input type="date" v-model="newDate">
<input type="text" v-model="newTime">
<button v-on:click="updateTask">Save</button>
<button v-on:click="isEditing = false">Cancel</button>
</template>
data() {
return {
newName: '',
newDate: '',
newTime: ''
}
},
methods: {
updateTask() {
// `newName`, `newDate`, and `newTime` are sent to the
// back-end to update task.
// Edit section is hidden, and task name is revealed again.
this.isEditing = false;
}
}
This works.
But the next time the user clicks on the “edit” button again, the input fields will show old values, which is definitely not good. We need to clear the inputs after the update is done. In which case, we need to do this:
methods: {
updateTask() {
// `newName`, `newDate`, and `newTime` are sent to the
// back-end to update task.
// Edit section is hidden, and task name is revealed again.
this.isEditing = false;
this.clearInputs();
},
clearInputs() {
this.newName = '';
this.newName = '';
this.newName = '';
}
}
Inputs also need to cleared when the user clicks “cancel”, in case they change their mind.
Plus, the edit section needs to show existing values in the form of pre-filled input fields. (Nobody will type the entire task name when they just want to change a word.) Which means, when the task prop is updated, the three input values must be updated too.
It’s starting to feel a bit redundant. We’re setting and resetting the three values too much. There should be a cleaner and easier way.
Check out Mantle on GitHub here