Module 1: Installation of App in Vite, Dependencies, Documentation, and Creating App Schema/Diagram
Step 1: Installation of App in Vite
Create a New Vite Project: Open your terminal and run the following command to create a new Vite project:
npm create vite@latest task-list-app -- --template vue
Navigate to the Project Directory:
cd task-list-app
Install Dependencies:
npm install
Start the Development Server:
npm run dev
Step 2: Install Tailwind CSS
Install Tailwind CSS and its peer dependencies:
npm install -D tailwindcss postcss autoprefixer
Initialize Tailwind CSS: Generate the tailwind.config.js
and postcss.config.js
files:
npx tailwindcss init -p
Configure tailwind.config.js
:
Replace the content with the following configuration:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
Create a CSS file for Tailwind: Create a new file named src/index.css
and add the following content:
@tailwind base;
@tailwind components;
@tailwind utilities;
Import the CSS file in your main entry point: Modify the src/main.js
file to import the Tailwind CSS file:
import { createApp } from 'vue'
import './index.css'
import App from './App.vue'
createApp(App).mount('#app')
Module 2: UI Design
In this module, we will create the UI components based on the provided design and ensure they are responsive. The design will be implemented using Vue 3 with the new Composition API (<script setup>
) and Tailwind CSS for styling.
Step 1: Set Up the Project Structure
Create the Components Directory: Inside the
src
directory, create acomponents
directory:
mkdir src/components
Create Initial Components:
AddTask.vue
EditTask.vue
TaskList.vue
TaskItem.vue
Create the Views Directory: Inside the
src
directory, create aviews
directory:
mkdir src/views
Create Initial Views:
Home.vue
Step 2: Implement the Components
AddTask Component:
<!-- src/components/AddTask.vue -->
<template>
<!-- Overlay container with fixed positioning, flex layout for centering content, and semi-transparent background -->
<div class="fixed inset-0 flex items-center justify-center bg-gray-500 bg-opacity-75">
<!-- Modal container with white background, padding, rounded corners, shadow effect, and fixed width -->
<div class="bg-white p-6 rounded-lg shadow-lg w-96">
<!-- Modal title with larger text, bold font, and bottom margin -->
<h2 class="text-2xl font-bold mb-4">Add Task</h2>
<!-- Input field for task name, bound to `task` ref, with styling for full width, padding, border, rounded corners, and bottom margin -->
<input
type="text"
placeholder="Task"
v-model="task"
class="w-full p-2 border border-gray-300 rounded mb-4"
/>
<!-- Container for priority buttons with bottom margin -->
<div class="mb-4">
<!-- Label for priority selection with right margin -->
<span class="mr-4">Priority:</span>
<!-- Button for setting priority to 'High', with conditional styling based on `priority` ref, and click event to set priority -->
<button
class="px-4 py-2 rounded mr-2"
:class="{'bg-red-500 text-white': priority === 'High', 'bg-gray-200': priority !== 'High'}"
@click="priority = 'High'"
>
High
</button>
<!-- Button for setting priority to 'Medium', with conditional styling based on `priority` ref, and click event to set priority -->
<button
class="px-4 py-2 rounded mr-2"
:class="{'bg-yellow-500 text-white': priority === 'Medium', 'bg-gray-200': priority !== 'Medium'}"
@click="priority = 'Medium'"
>
Medium
</button>
<!-- Button for setting priority to 'Low', with conditional styling based on `priority` ref, and click event to set priority -->
<button
class="px-4 py-2 rounded"
:class="{'bg-green-500 text-white': priority === 'Low', 'bg-gray-200': priority !== 'Low'}"
@click="priority = 'Low'"
>
Low
</button>
</div>
<!-- Button to add the task, with click event to trigger `handleAdd` method, and styling for full width, blue background, white text, padding, and rounded corners -->
<button
@click="handleAdd"
class="w-full bg-blue-500 text-white p-2 rounded"
>
Add
</button>
</div>
</div>
</template>
<script setup>
// Import the `ref` function from Vue to create reactive references
import { ref } from 'vue';
// Define a reactive reference for the task input, initialized as an empty string
const task = ref('');
// Define a reactive reference for the priority, initialized as an empty string
const priority = ref('');
// Define the function to handle adding a new task
const handleAdd = () => {
// Check if both the task and priority are set
if (task.value && priority.value) {
// Create a new task object with the current task and priority values, and a unique ID based on the current timestamp
const newTask = { task: task.value, priority: priority.value, id: Date.now() };
// Emit the new task object to the parent component
emit('add-task', newTask);
// Reset the task input and priority to empty strings
task.value = '';
priority.value = '';
}
};
</script>
EXPLANATION:
Template Section
The template section is responsible for defining the structure and layout of the component that the user will see.
Detailed Explanation
Overlay Container:
<div class="fixed inset-0 flex items-center justify-center bg-gray-500 bg-opacity-75">
:fixed inset-0
: Makes the container cover the entire screen.flex items-center justify-center
: Centers the content inside the container.bg-gray-500 bg-opacity-75
: Adds a semi-transparent gray background.
Modal Container:
<div class="bg-white p-6 rounded-lg shadow-lg w-96">
:bg-white
: White background.p-6
: Padding inside the container.rounded-lg
: Rounded corners.shadow-lg
: Adds a shadow for a 3D effect.w-96
: Fixed width of the container.
Title:
<h2 class="text-2xl font-bold mb-4">Add Task</h2>
:text-2xl
: Large text size.font-bold
: Bold font.mb-4
: Margin at the bottom.
Task Input Field:
<input type="text" placeholder="Task" v-model="task" class="w-full p-2 border border-gray-300 rounded mb-4" />
:v-model="task"
: Binds the input value to thetask
variable.w-full
: Full width.p-2
: Padding.border border-gray-300
: Gray border.rounded
: Rounded corners.mb-4
: Margin at the bottom.
Priority Selection:
<div class="mb-4">
: Container for the buttons with bottom margin.<span class="mr-4">Priority:</span>
: Label with right margin.- Buttons:
:class
: Dynamically applies classes based on thepriority
value.@click="priority = 'High'"
: Sets thepriority
to ‘High’ when clicked.
Add Button:
<button @click="handleAdd" class="w-full bg-blue-500 text-white p-2 rounded">Add</button>
:@click="handleAdd"
: Calls thehandleAdd
function when clicked.w-full
: Full width.bg-blue-500
: Blue background.text-white
: White text.p-2
: Padding.rounded
: Rounded corners.
EXPLANATION:
Script Section Explanation
Imports:
import { ref } from 'vue';
: Imports theref
function to create reactive variables.
Reactive Variables:
const task = ref('');
: Creates a reactive variabletask
initialized to an empty string.const priority = ref('');
: Creates a reactive variablepriority
initialized to an empty string.
handleAdd Function:
const handleAdd = () => { ... };
: Defines the function to handle adding a new task.if (task.value && priority.value) { ... }
: Checks if bothtask
andpriority
are not empty.const newTask = { task: task.value, priority: priority.value, id: Date.now() };
: Creates a new task object withtask
,priority
, and a unique ID.emit('add-task', newTask);
: Emits an event called ‘add-task’ with the new task object to the parent component.task.value = ''; priority.value = '';
: Resets thetask
andpriority
to empty strings.
This component creates a modal for adding a task, with an input field for the task name and buttons to select the priority. When the “Add” button is clicked, the task and priority are checked, and if both are set, a new task object is created and emitted to the parent component. The input fields are then reset for the next entry.
EditTask Component:
<!-- src/components/EditTask.vue -->
<template>
<!-- Overlay container with fixed positioning, flex layout for centering content, and semi-transparent background -->
<div class="fixed inset-0 flex items-center justify-center bg-gray-500 bg-opacity-75">
<!-- Modal container with white background, padding, rounded corners, shadow effect, and fixed width -->
<div class="bg-white p-6 rounded-lg shadow-lg w-96">
<!-- Modal title with larger text, bold font, and bottom margin -->
<h2 class="text-2xl font-bold mb-4">Edit Task</h2>
<!-- Input field for task name, bound to `newTask` ref, with styling for full width, padding, border, rounded corners, and bottom margin -->
<input
type="text"
placeholder="Task"
v-model="newTask"
class="w-full p-2 border border-gray-300 rounded mb-4"
/>
<!-- Container for priority buttons with bottom margin -->
<div class="mb-4">
<!-- Label for priority selection with right margin -->
<span class="mr-4">Priority:</span>
<!-- Button for setting priority to 'High', with conditional styling based on `priority` ref, and click event to set priority -->
<button
class="px-4 py-2 rounded mr-2"
:class="{'bg-red-500 text-white': priority === 'High', 'bg-gray-200': priority !== 'High'}"
@click="priority = 'High'"
>
High
</button>
<!-- Button for setting priority to 'Medium', with conditional styling based on `priority` ref, and click event to set priority -->
<button
class="px-4 py-2 rounded mr-2"
:class="{'bg-yellow-500 text-white': priority === 'Medium', 'bg-gray-200': priority !== 'Medium'}"
@click="priority = 'Medium'"
>
Medium
</button>
<!-- Button for setting priority to 'Low', with conditional styling based on `priority` ref, and click event to set priority -->
<button
class="px-4 py-2 rounded"
:class="{'bg-green-500 text-white': priority === 'Low', 'bg-gray-200': priority !== 'Low'}"
@click="priority = 'Low'"
>
Low
</button>
</div>
<!-- Button to update the task, with click event to trigger `handleUpdate` method, and styling for full width, blue background, white text, padding, and rounded corners -->
<button
@click="handleUpdate"
class="w-full bg-blue-500 text-white p-2 rounded"
>
Update
</button>
</div>
</div>
</template>
<script setup>
// Import the `ref` function from Vue for creating reactive references
import { ref } from 'vue';
// Define the component props, expecting a `task` object
const props = defineProps(['task']);
// Define the `emit` function to emit events to the parent component
const emit = defineEmits(['update-task']);
// Define a reactive reference for the new task name, initialized with the task name from props
const newTask = ref(props.task.task);
// Define a reactive reference for the priority, initialized with the priority from props
const priority = ref(props.task.priority);
// Define the function to handle updating the task
const handleUpdate = () => {
// Check if both the new task name and priority are set
if (newTask.value && priority.value) {
// Create an updated task object by copying the original task props and updating the task name and priority
const updatedTask = { ...props.task, task: newTask.value, priority: priority.value };
// Emit the updated task object to the parent component
emit('update-task', updatedTask);
}
};
</script>
EXPLANATION Template Section
The template section defines the structure and layout of the component that the user will see.
Detailed Explanation
Overlay Container:
<div class="fixed inset-0 flex items-center justify-center bg-gray-500 bg-opacity-75">
:fixed inset-0
: Makes the container cover the entire screen.flex items-center justify-center
: Centers the content inside the container.bg-gray-500 bg-opacity-75
: Adds a semi-transparent gray background.
Modal Container:
<div class="bg-white p-6 rounded-lg shadow-lg w-96">
:bg-white
: White background.p-6
: Padding inside the container.rounded-lg
: Rounded corners.shadow-lg
: Adds a shadow for a 3D effect.w-96
: Fixed width of the container.
Title:
<h2 class="text-2xl font-bold mb-4">Edit Task</h2>
:text-2xl
: Large text size.font-bold
: Bold font.mb-4
: Margin at the bottom.
Task Input Field:
<input type="text" placeholder="Task" v-model="newTask" class="w-full p-2 border border-gray-300 rounded mb-4" />
:v-model="newTask"
: Binds the input value to thenewTask
variable.w-full
: Full width.p-2
: Padding.border border-gray-300
: Gray border.rounded
: Rounded corners.mb-4
: Margin at the bottom.
Priority Selection:
<div class="mb-4">
: Container for the buttons with bottom margin.<span class="mr-4">Priority:</span>
: Label with right margin.- Buttons:
:class
: Dynamically applies classes based on thepriority
value.@click="priority = 'High'"
: Sets thepriority
to ‘High’ when clicked.
Update Button:
<button @click="handleUpdate" class="w-full bg-blue-500 text-white p-2 rounded">Update</button>
:@click="handleUpdate"
: Calls thehandleUpdate
function when clicked.w-full
: Full width.bg-blue-500
: Blue background.text-white
: White text.p-2
: Padding.rounded
: Rounded corners.
EXPLANATION Script Section
The script section is where we define the data and functions that make the component work.
Script Section Explanation
Imports:
import { ref } from 'vue';
: Imports theref
function to create reactive variables.
Props:
const props = defineProps(['task']);
: Defines the properties that this component expects from its parent. In this case, atask
object.
Emit Function:
const emit = defineEmits(['update-task']);
: Defines the function to emit events to the parent component. This allows communication back to the parent component.
Reactive Variables:
const newTask = ref(props.task.task);
: Creates a reactive variablenewTask
initialized with thetask
name from theprops
.const priority = ref(props.task.priority);
: Creates a reactive variablepriority
initialized with thepriority
from theprops
.
handleUpdate Function:
const handleUpdate = () => { ... };
: Defines the function to handle updating the task.if (newTask.value && priority.value) { ... }
: Checks if bothnewTask
andpriority
are not empty.const updatedTask = { ...props.task, task: newTask.value, priority: priority.value };
: Creates an updated task object by copying the originaltask
properties and updating thetask
name andpriority
.emit('update-task', updatedTask);
: Emits an event called ‘update-task’ with the updated task object to the parent component.
This component creates a modal for editing an existing task, with an input field for the task name and buttons to select the priority. When the “Update” button is clicked, the task name and priority are checked, and if both are set, an updated task object is created and emitted to the parent component.
TaskItem Component:
<!-- src/components/TaskItem.vue -->
<template>
<!-- Container for the task item with flex layout, padding, and bottom border -->
<div class="flex items-center justify-between p-4 border-b border-gray-300">
<!-- Container for the task details -->
<div>
<!-- Task title with larger, bold text -->
<h3 class="text-lg font-bold">{{ task.task }}</h3>
<!-- Task priority with dynamic classes based on the priority value -->
<p :class="{
'text-sm': true, // Common class for small text size
'text-red-500': task.priority === 'High', // Red text for high priority
'text-yellow-500': task.priority === 'Medium', // Yellow text for medium priority
'text-green-500': task.priority === 'Low' // Green text for low priority
}">
Priority: {{ task.priority }} // Display the priority value
</p>
</div>
<!-- Container for action buttons -->
<div class="flex items-center">
<!-- Edit button with blue text, emits 'edit-task' event with the task object when clicked -->
<button @click="$emit('edit-task', task)" class="mr-2 text-blue-500">Edit</button>
<!-- Delete button with red text, emits 'delete-task' event with the task ID when clicked -->
<button @click="$emit('delete-task', task.id)" class="text-red-500">Delete</button>
</div>
</div>
</template>
<script setup>
// Define the component props, expecting a `task` object
const props = defineProps(['task']);
</script>
Let’s break down the TaskItem.vue
component step by step, explaining each part in a way that’s easy to understand for a beginner.
Template Section
The template section defines the structure and layout of the component that the user will see.
Detailed Explanation
Container for the Task Item:
<div class="flex items-center justify-between p-4 border-b border-gray-300">
:flex
: Uses flexbox for layout.items-center
: Centers items vertically.justify-between
: Spreads items apart horizontally.p-4
: Adds padding around the content.border-b border-gray-300
: Adds a bottom border with a gray color.
Container for Task Details:
<div>
: Simple container for the task details (title and priority).
Task Title:
<h3 class="text-lg font-bold">{{ task.task }}</h3>
:text-lg
: Large text size.font-bold
: Bold text.{{ task.task }}
: Displays the task’s title from thetask
prop.
Task Priority:
<p :class="{...}">Priority: {{ task.priority }}</p>
::class="{...}"
: Dynamically applies classes based on the task’s priority.text-sm
: Small text size (always applied).text-red-500
: Red text for high priority.text-yellow-500
: Yellow text for medium priority.text-green-500
: Green text for low priority.Priority: {{ task.priority }}
: Displays the task’s priority from thetask
prop.
Container for Action Buttons:
<div class="flex items-center">
:flex
: Uses flexbox for layout.items-center
: Centers items vertically.
Edit Button:
<button @click="$emit('edit-task', task)" class="mr-2 text-blue-500">Edit</button>
:@click="$emit('edit-task', task)"
: Emits anedit-task
event with thetask
object when clicked.mr-2
: Adds a right margin.text-blue-500
: Blue text color.Edit
: Button text.
Delete Button:
<button @click="$emit('delete-task', task.id)" class="text-red-500">Delete</button>
:@click="$emit('delete-task', task.id)"
: Emits adelete-task
event with the task’s ID when clicked.text-red-500
: Red text color.Delete
: Button text.
Script Section Explanation
- Props:
const props = defineProps(['task']);
: Defines the properties that this component expects to receive from its parent. In this case, atask
object.
This TaskItem
component displays a task with its title and priority. It also provides “Edit” and “Delete” buttons. When these buttons are clicked, events are emitted to the parent component to handle editing or deleting the task. The task’s priority is styled differently depending on whether it is high, medium, or low.
Step 3: Implement the Home View
Home View:
<!-- src/views/Home.vue -->
<template>
<!-- Main container with minimum height screen, gray background, and padding -->
<div class="min-h-screen bg-gray-100 p-4">
<!-- Centered container -->
<div class="container mx-auto">
<!-- Header section with flex layout to space items between -->
<div class="flex justify-between items-center mb-4">
<!-- Title of the task list -->
<h1 class="text-3xl font-bold">Task List</h1>
<!-- Button to open the add task modal -->
<button
@click="isAdding = true"
class="bg-blue-500 text-white px-4 py-2 rounded"
>
+ Add Task
</button>
</div>
<!-- Task list component, passing tasks as props and handling edit and delete events -->
<TaskList :tasks="tasks" @edit-task="startEditing" @delete-task="deleteTask" />
<!-- AddTask component, shown conditionally if isAdding is true, handling add task and close events -->
<AddTask v-if="isAdding" @add-task="addTask" @close="isAdding = false" />
<!-- EditTask component, shown conditionally if isEditing is true, passing the current task as props and handling update task and close events -->
<EditTask v-if="isEditing" :task="currentTask" @update-task="updateTask" @close="isEditing = false" />
</div>
</div>
</template>
<script setup>
// Import the `ref` function from Vue for creating reactive references
import { ref } from 'vue';
// Import child components
import AddTask from '../components/AddTask.vue';
import EditTask from '../components/EditTask.vue';
import TaskList from '../components/TaskList.vue';
// Define a reactive reference for the list of tasks
const tasks = ref([]);
// Define a reactive reference for the add task modal visibility
const isAdding = ref(false);
// Define a reactive reference for the edit task modal visibility
const isEditing = ref(false);
// Define a reactive reference for the current task being edited
const currentTask = ref(null);
// Function to add a new task to the list
const addTask = (task) => {
// Push the new task to the tasks array
tasks.value.push(task);
// Close the add task modal
isAdding.value = false;
};
// Function to update an existing task in the list
const updateTask = (updatedTask) => {
// Find the index of the task to be updated
const index = tasks.value.findIndex(task => task.id === updatedTask.id);
// If the task exists, update it
if (index !== -1) {
tasks.value[index] = updatedTask;
// Close the edit task modal
isEditing.value = false;
}
};
// Function to delete a task from the list
const deleteTask = (taskId) => {
// Filter out the task with the given ID
tasks.value = tasks.value.filter(task => task.id !== taskId);
};
// Function to start editing a task
const startEditing = (task) => {
// Set the current task to be edited
currentTask.value = task;
// Open the edit task modal
isEditing.value = true;
};
</script>
Let’s break down the Home.vue
component step by step, explaining each part in a way that’s easy to understand for a beginner.
Template Section
The template section defines the structure and layout of the component that the user will see.
Detailed Explanation
Main Container:
<div class="min-h-screen bg-gray-100 p-4">
:min-h-screen
: Ensures the container takes the full height of the screen.bg-gray-100
: Sets a light gray background color.p-4
: Adds padding around the content.
Centered Container:
<div class="container mx-auto">
: Centers the content horizontally.
Header Section:
<div class="flex justify-between items-center mb-4">
:flex
: Uses flexbox for layout.justify-between
: Spaces items apart horizontally.items-center
: Centers items vertically.mb-4
: Adds a margin at the bottom.
Title:
<h1 class="text-3xl font-bold">Task List</h1>
:text-3xl
: Sets a large text size.font-bold
: Makes the text bold.
Add Task Button:
<button @click="isAdding = true" class="bg-blue-500 text-white px-4 py-2 rounded">+ Add Task</button>
:@click="isAdding = true"
: When clicked, setsisAdding
totrue
to show the add task modal.bg-blue-500
: Sets a blue background color.text-white
: Sets white text color.px-4 py-2
: Adds padding on the x (left and right) and y (top and bottom) axes.rounded
: Rounds the corners of the button.
TaskList Component:
<TaskList :tasks="tasks" @edit-task="startEditing" @delete-task="deleteTask" />
::tasks="tasks"
: Passes thetasks
array as a prop to theTaskList
component.@edit-task="startEditing"
: Listens for theedit-task
event and calls thestartEditing
function.@delete-task="deleteTask"
: Listens for thedelete-task
event and calls thedeleteTask
function.
AddTask Component:
<AddTask v-if="isAdding" @add-task="addTask" @close="isAdding = false" />
:v-if="isAdding"
: Conditionally shows theAddTask
component ifisAdding
istrue
.@add-task="addTask"
: Listens for theadd-task
event and calls theaddTask
function.@close="isAdding = false"
: Listens for theclose
event and setsisAdding
tofalse
.
EditTask Component:
<EditTask v-if="isEditing" :task="currentTask" @update-task="updateTask" @close="isEditing = false" />
:v-if="isEditing"
: Conditionally shows theEditTask
component ifisEditing
istrue
.:task="currentTask"
: Passes thecurrentTask
as a prop to theEditTask
component.@update-task="updateTask"
: Listens for theupdate-task
event and calls theupdateTask
function.@close="isEditing = false"
: Listens for theclose
event and setsisEditing
tofalse
.
Script Section
The script section is where we define the data and functions that make the component work.
Script Section Explanation
Imports:
import { ref } from 'vue';
: Imports theref
function to create reactive variables.import AddTask from '../components/AddTask.vue';
: Imports theAddTask
component.import EditTask from '../components/EditTask.vue';
: Imports theEditTask
component.import TaskList from '../components/TaskList.vue';
: Imports theTaskList
component.
Reactive Variables:
const tasks = ref([]);
: Creates a reactive variabletasks
initialized as an empty array to store the list of tasks.const isAdding = ref(false);
: Creates a reactive variableisAdding
to control the visibility of the add task modal, initialized tofalse
.const isEditing = ref(false);
: Creates a reactive variableisEditing
to control the visibility of the edit task modal, initialized tofalse
.const currentTask = ref(null);
: Creates a reactive variablecurrentTask
to store the task being edited, initialized tonull
.
Functions:
const addTask = (task) => { ... };
: Defines the function to add a new task to the list.- Adds the new task to the
tasks
array and closes the add task modal.
- Adds the new task to the
const updateTask = (updatedTask) => { ... };
: Defines the function to update an existing task in the list.- Finds the task by its ID, updates it, and closes the edit task modal.
const deleteTask = (taskId) => { ... };
: Defines the function to delete a task from the list.- Removes the task with the given ID from the
tasks
array.
- Removes the task with the given ID from the
const startEditing = (task) => { ... };
: Defines the function to start editing a task.- Sets the
currentTask
to the task being edited and opens the edit task modal.
- Sets the
This Home
component is the main view that displays the task list, and allows users to add, edit, and delete tasks. It manages the visibility of the add and edit task modals and handles the corresponding events to update the task list accordingly. The component imports and uses AddTask
, EditTask
, and TaskList
components to handle the specific functionalities related to tasks.
Step 4: Update the App Component
App Component:
<!-- src/App.vue -->
<template>
<router-view />
</template>
Router Setup:
// Import the `createApp` function from Vue to create a Vue application instance
import { createApp } from 'vue';
// Import the `createRouter` and `createWebHistory` functions from Vue Router for routing
import { createRouter, createWebHistory } from 'vue-router';
// Import the main CSS file
import './index.css';
// Import the root component of the application
import App from './App.vue';
// Import the Home component, which will be used as the main view for the home route
import Home from './views/Home.vue';
// Define the routes for the application
const routes = [
{ path: '/', component: Home }, // Route for the home page
{ path: '/:pathMatch(.*)*', redirect: '/' }, // Catch-all route to redirect any unknown paths to the home page
];
// Create a router instance with the defined routes and history mode
const router = createRouter({
history: createWebHistory(), // Use HTML5 history mode for clean URLs
routes, // The routes defined above
});
// Create the Vue application instance with the root component `App`
const app = createApp(App);
// Use the router instance in the Vue application
app.use(router);
// Mount the Vue application to the HTML element with the ID 'app'
app.mount('#app');
Explanation:
Import Statements:
createApp
: Function to create a new Vue application instance.createRouter
andcreateWebHistory
: Functions from Vue Router to set up client-side routing../index.css
: Importing the main CSS file for global styles.App
: The root component of the Vue application.Home
: The component to be displayed at the home route.
Routes Configuration:
- Defines the routes for the application.
- The home route (
'/'
) uses theHome
component. - A catch-all route (
'/:pathMatch(.*)*'
) redirects any unmatched paths to the home route.
Router Instance:
createRouter
: Creates a router instance with HTML5 history mode and the defined routes.
Vue Application Instance:
createApp
: Creates the Vue application instance using theApp
component.app.use(router)
: Registers the router instance with the Vue application.app.mount('#app')
: Mounts the Vue application to the DOM element with the ID'app'
.
This annotated version of main.js
provides a detailed explanation of each part, making it easier to understand the setup and configuration of the Vue application with routing.
You have now created the basic UI components based on the provided design, ensuring they are responsive and styled with Tailwind CSS. The components use Vue 3’s Composition API with <script setup>
. Next, we will move on to Module 3: CRUD operations.
Module 3: CRUD Operations
In this module, we will implement the CRUD operations (Create, Read, Update, Delete) on the previously designed UI components in our Vue 3 application.
Step 1: Implement CRUD Operations in Home View
We will manage the state and methods for adding, updating, and deleting tasks in the Home.vue
component.
Home View:
<!-- src/views/Home.vue -->
<template>
<!-- Main container with minimum height screen, gray background, and padding -->
<div class="min-h-screen bg-gray-100 p-4">
<!-- Centered container -->
<div class="container mx-auto">
<!-- Header section with flex layout to space items between -->
<div class="flex justify-between items-center mb-4">
<!-- Title of the task list -->
<h1 class="text-3xl font-bold">Task List</h1>
<!-- Button to open the add task modal -->
<button
@click="isAdding = true"
class="bg-blue-500 text-white px-4 py-2 rounded"
>
+ Add Task
</button>
</div>
<!-- Task list component, passing tasks as props and handling edit and delete events -->
<TaskList :tasks="tasks" @edit-task="startEditing" @delete-task="deleteTask" />
<!-- AddTask component, shown conditionally if isAdding is true, handling add task and close events -->
<AddTask v-if="isAdding" @add-task="addTask" @close="isAdding = false" />
<!-- EditTask component, shown conditionally if isEditing is true, passing the current task as props and handling update task and close events -->
<EditTask v-if="isEditing" :task="currentTask" @update-task="updateTask" @close="isEditing = false" />
</div>
</div>
</template>
<script setup>
// Import the `ref` function from Vue for creating reactive references
import { ref } from 'vue';
// Import child components
import AddTask from '../components/AddTask.vue';
import EditTask from '../components/EditTask.vue';
import TaskList from '../components/TaskList.vue';
// Define a reactive reference for the list of tasks
const tasks = ref([]);
// Define a reactive reference for the add task modal visibility
const isAdding = ref(false);
// Define a reactive reference for the edit task modal visibility
const isEditing = ref(false);
// Define a reactive reference for the current task being edited
const currentTask = ref(null);
// Function to add a new task to the list
const addTask = (task) => {
// Push the new task to the tasks array
tasks.value.push(task);
// Close the add task modal
isAdding.value = false;
};
// Function to update an existing task in the list
const updateTask = (updatedTask) => {
// Find the index of the task to be updated
const index = tasks.value.findIndex(task => task.id === updatedTask.id);
// If the task exists, update it
if (index !== -1) {
tasks.value[index] = updatedTask;
// Close the edit task modal
isEditing.value = false;
}
};
// Function to delete a task from the list
const deleteTask = (taskId) => {
// Filter out the task with the given ID
tasks.value = tasks.value.filter(task => task.id !== taskId);
};
// Function to start editing a task
const startEditing = (task) => {
// Set the current task to be edited
currentTask.value = task;
// Open the edit task modal
isEditing.value = true;
};
</script>
Let’s break down the Home.vue
component step by step, explaining each part in a way that’s easy to understand for a beginner.
Template Section
The template section defines the structure and layout of the component that the user will see.
Detailed Explanation
Main Container:
<div class="min-h-screen bg-gray-100 p-4">
:min-h-screen
: Ensures the container takes the full height of the screen.bg-gray-100
: Sets a light gray background color.p-4
: Adds padding around the content.
Centered Container:
<div class="container mx-auto">
: Centers the content horizontally.
Header Section:
<div class="flex justify-between items-center mb-4">
:flex
: Uses flexbox for layout.justify-between
: Spaces items apart horizontally.items-center
: Centers items vertically.mb-4
: Adds a margin at the bottom.
Title:
<h1 class="text-3xl font-bold">Task List</h1>
:text-3xl
: Sets a large text size.font-bold
: Makes the text bold.
Add Task Button:
<button @click="isAdding = true" class="bg-blue-500 text-white px-4 py-2 rounded">+ Add Task</button>
:@click="isAdding = true"
: When clicked, setsisAdding
totrue
to show the add task modal.bg-blue-500
: Sets a blue background color.text-white
: Sets white text color.px-4 py-2
: Adds padding on the x (left and right) and y (top and bottom) axes.rounded
: Rounds the corners of the button.
TaskList Component:
<TaskList :tasks="tasks" @edit-task="startEditing" @delete-task="deleteTask" />
::tasks="tasks"
: Passes thetasks
array as a prop to theTaskList
component.@edit-task="startEditing"
: Listens for theedit-task
event and calls thestartEditing
function.@delete-task="deleteTask"
: Listens for thedelete-task
event and calls thedeleteTask
function.
AddTask Component:
<AddTask v-if="isAdding" @add-task="addTask" @close="isAdding = false" />
:v-if="isAdding"
: Conditionally shows theAddTask
component ifisAdding
istrue
.@add-task="addTask"
: Listens for theadd-task
event and calls theaddTask
function.@close="isAdding = false"
: Listens for theclose
event and setsisAdding
tofalse
.
EditTask Component:
<EditTask v-if="isEditing" :task="currentTask" @update-task="updateTask" @close="isEditing = false" />
:v-if="isEditing"
: Conditionally shows theEditTask
component ifisEditing
istrue
.:task="currentTask"
: Passes thecurrentTask
as a prop to theEditTask
component.@update-task="updateTask"
: Listens for theupdate-task
event and calls theupdateTask
function.@close="isEditing = false"
: Listens for theclose
event and setsisEditing
tofalse
.
Let’s break down the Home.vue
component step by step, explaining each part in a way that’s easy to understand for a beginner.
Script Section
The script section is where we define the data and functions that make the component work.
Imports:
import { ref } from 'vue';
: Imports theref
function to create reactive variables.import AddTask from '../components/AddTask.vue';
: Imports theAddTask
component.import EditTask from '../components/EditTask.vue';
: Imports theEditTask
component.import TaskList from '../components/TaskList.vue';
: Imports theTaskList
component.
Reactive Variables:
const tasks = ref([]);
: Creates a reactive variabletasks
initialized as an empty array to store the list of tasks.const isAdding = ref(false);
: Creates a reactive variableisAdding
to control the visibility of the add task modal, initialized tofalse
.const isEditing = ref(false);
: Creates a reactive variableisEditing
to control the visibility of the edit task modal, initialized tofalse
.const currentTask = ref(null);
: Creates a reactive variablecurrentTask
to store the task being edited, initialized tonull
.
Functions:
const addTask = (task) => { ... };
: Defines the function to add a new task to the list.- Adds the new task to the
tasks
array and closes the add task modal.
- Adds the new task to the
const updateTask = (updatedTask) => { ... };
: Defines the function to update an existing task in the list.- Finds the task by its ID, updates it, and closes the edit task modal.
const deleteTask = (taskId) => { ... };
: Defines the function to delete a task from the list.- Removes the task with the given ID from the
tasks
array.
- Removes the task with the given ID from the
const startEditing = (task) => { ... };
: Defines the function to start editing a task.- Sets the
currentTask
to the task being edited and opens the edit task modal.
- Sets the
Summary
This Home
component is the main view that displays the task list and allows users to add, edit, and delete tasks. It manages the visibility of the add and edit task modals and handles the corresponding events to update the task list accordingly. The component imports and uses AddTask
, EditTask
, and TaskList
components to handle the specific functionalities related to tasks.
Step 2: Emit Events from Components
We need to make sure the components emit the necessary events for CRUD operations.
AddTask Component:
<!-- src/components/AddTask.vue -->
<template>
<!-- Overlay container with fixed positioning, flex layout for centering content, and semi-transparent background -->
<div class="fixed inset-0 flex items-center justify-center bg-gray-500 bg-opacity-75">
<!-- Modal container with white background, padding, rounded corners, shadow effect, and fixed width -->
<div class="bg-white p-6 rounded-lg shadow-lg w-96">
<!-- Modal title with larger text, bold font, and bottom margin -->
<h2 class="text-2xl font-bold mb-4">Add Task</h2>
<!-- Input field for task name, bound to `task` ref, with styling for full width, padding, border, rounded corners, and bottom margin -->
<input
type="text"
placeholder="Task"
v-model="task"
class="w-full p-2 border border-gray-300 rounded mb-4"
/>
<!-- Container for priority buttons with bottom margin -->
<div class="mb-4">
<!-- Label for priority selection with right margin -->
<span class="mr-4">Priority:</span>
<!-- Button for setting priority to 'High', with conditional styling based on `priority` ref, and click event to set priority -->
<button
class="px-4 py-2 rounded mr-2"
:class="{'bg-red-500 text-white': priority === 'High', 'bg-gray-200': priority !== 'High'}"
@click="priority = 'High'"
>
High
</button>
<!-- Button for setting priority to 'Medium', with conditional styling based on `priority` ref, and click event to set priority -->
<button
class="px-4 py-2 rounded mr-2"
:class="{'bg-yellow-500 text-white': priority === 'Medium', 'bg-gray-200': priority !== 'Medium'}"
@click="priority = 'Medium'"
>
Medium
</button>
<!-- Button for setting priority to 'Low', with conditional styling based on `priority` ref, and click event to set priority -->
<button
class="px-4 py-2 rounded"
:class="{'bg-green-500 text-white': priority === 'Low', 'bg-gray-200': priority !== 'Low'}"
@click="priority = 'Low'"
>
Low
</button>
</div>
<!-- Button to add the task, with click event to trigger `handleAdd` method, and styling for full width, blue background, white text, padding, and rounded corners -->
<button
@click="handleAdd"
class="w-full bg-blue-500 text-white p-2 rounded"
>
Add
</button>
</div>
</div>
</template>
<script setup>
// Import the `ref` function from Vue for creating reactive references and `defineEmits` for emitting events
import { ref, defineEmits } from 'vue';
// Define a reactive reference for the task input, initialized as an empty string
const task = ref('');
// Define a reactive reference for the priority, initialized as an empty string
const priority = ref('');
// Define the `emit` function to emit events to the parent component
const emit = defineEmits(['add-task', 'close']);
// Define the function to handle adding a new task
const handleAdd = () => {
// Check if both the task and priority are set
if (task.value && priority.value) {
// Create a new task object with the current task and priority values, and a unique ID based on the current timestamp
const newTask = { task: task.value, priority: priority.value, id: Date.now() };
// Emit the new task object to the parent component
emit('add-task', newTask);
// Emit a close event to close the add task modal
emit('close');
}
};
</script>
Let’s break down the AddTask.vue
component step by step, explaining each part in a way that’s easy to understand for a beginner.
Template Section
The template section defines the structure and layout of the component that the user will see.
Detailed Explanation
Overlay Container:
<div class="fixed inset-0 flex items-center justify-center bg-gray-500 bg-opacity-75">
:fixed inset-0
: Makes the container cover the entire screen.flex items-center justify-center
: Centers the content inside the container.bg-gray-500 bg-opacity-75
: Adds a semi-transparent gray background.
Modal Container:
<div class="bg-white p-6 rounded-lg shadow-lg w-96">
:bg-white
: White background.p-6
: Padding inside the container.rounded-lg
: Rounded corners.shadow-lg
: Adds a shadow for a 3D effect.w-96
: Fixed width of the container.
Title:
<h2 class="text-2xl font-bold mb-4">Add Task</h2>
:text-2xl
: Large text size.font-bold
: Bold text.mb-4
: Margin at the bottom.
Task Input Field:
<input type="text" placeholder="Task" v-model="task" class="w-full p-2 border border-gray-300 rounded mb-4" />
:v-model="task"
: Binds the input value to thetask
variable.w-full
: Full width.p-2
: Padding.border border-gray-300
: Gray border.rounded
: Rounded corners.mb-4
: Margin at the bottom.
Priority Selection:
<div class="mb-4">
: Container for the buttons with bottom margin.<span class="mr-4">Priority:</span>
: Label with right margin.- Buttons:
:class
: Dynamically applies classes based on thepriority
value.@click="priority = 'High'"
: Sets thepriority
to ‘High’ when clicked.
Add Button:
<button @click="handleAdd" class="w-full bg-blue-500 text-white p-2 rounded">Add</button>
:@click="handleAdd"
: Calls thehandleAdd
function when clicked.w-full
: Full width.bg-blue-500
: Blue background.text-white
: White text.p-2
: Padding.rounded
: Rounded corners.
Let’s break down the AddTask.vue
component step by step, explaining each part in a way that’s easy to understand for a beginner.
Script Section
The script section is where we define the data and functions that make the component work.
Script Section Explanation
Imports:
import { ref, defineEmits } from 'vue';
: Imports theref
function to create reactive variables anddefineEmits
to emit events to the parent component.
Reactive Variables:
const task = ref('');
: Creates a reactive variabletask
initialized to an empty string.const priority = ref('');
: Creates a reactive variablepriority
initialized to an empty string.
Emit Function:
const emit = defineEmits(['add-task', 'close']);
: Defines the function to emit events. Here, we define two events:add-task
andclose
.
handleAdd Function:
const handleAdd = () => { ... };
: Defines the function to handle adding a new task.if (task.value && priority.value) { ... }
: Checks if bothtask
andpriority
are not empty.const newTask = { task: task.value, priority: priority.value, id: Date.now() };
: Creates a new task object withtask
,priority
, and a unique ID.emit('add-task', newTask);
: Emits an event called ‘add-task’ with the new task object to the parent component.emit('close');
: Emits a ‘close’ event to close the add task modal.
This AddTask
component creates a modal for adding a new task. It includes an input field for the task name and buttons to select the priority (High, Medium, Low). When the “Add” button is clicked, the handleAdd
function checks if both the task name and priority are set. If they are, it creates a new task object, emits an ‘add-task’ event with the new task object to the parent component, and emits a ‘close’ event to close the modal. The component uses reactive variables to store the task name and priority and dynamically updates the UI based on these values.
EditTask Component:
<!-- src/components/EditTask.vue -->
<template>
<!-- Overlay container with fixed positioning, flex layout for centering content, and semi-transparent background -->
<div class="fixed inset-0 flex items-center justify-center bg-gray-500 bg-opacity-75">
<!-- Modal container with white background, padding, rounded corners, shadow effect, and fixed width -->
<div class="bg-white p-6 rounded-lg shadow-lg w-96">
<!-- Modal title with larger text, bold font, and bottom margin -->
<h2 class="text-2xl font-bold mb-4">Edit Task</h2>
<!-- Input field for task name, bound to `newTask` ref, with styling for full width, padding, border, rounded corners, and bottom margin -->
<input
type="text"
placeholder="Task"
v-model="newTask"
class="w-full p-2 border border-gray-300 rounded mb-4"
/>
<!-- Container for priority buttons with bottom margin -->
<div class="mb-4">
<!-- Label for priority selection with right margin -->
<span class="mr-4">Priority:</span>
<!-- Button for setting priority to 'High', with conditional styling based on `priority` ref, and click event to set priority -->
<button
class="px-4 py-2 rounded mr-2"
:class="{'bg-red-500 text-white': priority === 'High', 'bg-gray-200': priority !== 'High'}"
@click="priority = 'High'"
>
High
</button>
<!-- Button for setting priority to 'Medium', with conditional styling based on `priority` ref, and click event to set priority -->
<button
class="px-4 py-2 rounded mr-2"
:class="{'bg-yellow-500 text-white': priority === 'Medium', 'bg-gray-200': priority !== 'Medium'}"
@click="priority = 'Medium'"
>
Medium
</button>
<!-- Button for setting priority to 'Low', with conditional styling based on `priority` ref, and click event to set priority -->
<button
class="px-4 py-2 rounded"
:class="{'bg-green-500 text-white': priority === 'Low', 'bg-gray-200': priority !== 'Low'}"
@click="priority = 'Low'"
>
Low
</button>
</div>
<!-- Button to update the task, with click event to trigger `handleUpdate` method, and styling for full width, blue background, white text, padding, and rounded corners -->
<button
@click="handleUpdate"
class="w-full bg-blue-500 text-white p-2 rounded"
>
Update
</button>
</div>
</div>
</template>
<script setup>
// Import the `ref` function from Vue for creating reactive references and `defineProps`, `defineEmits` for handling props and events
import { ref, defineProps, defineEmits } from 'vue';
// Define the component props, expecting a `task` object
const props = defineProps(['task']);
// Define the `emit` function to emit events to the parent component
const emit = defineEmits(['update-task', 'close']);
// Define a reactive reference for the new task name, initialized with the task name from props
const newTask = ref(props.task.task);
// Define a reactive reference for the priority, initialized with the priority from props
const priority = ref(props.task.priority);
// Define the function to handle updating the task
const handleUpdate = () => {
// Check if both the new task name and priority are set
if (newTask.value && priority.value) {
// Create an updated task object by copying the original task props and updating the task name and priority
const updatedTask = { ...props.task, task: newTask.value, priority: priority.value };
// Emit the updated task object to the parent component
emit('update-task', updatedTask);
// Emit a close event to close the edit task modal
emit('close');
}
};
</script>
Let’s break down the EditTask.vue
component step by step, explaining each part in a way that’s easy to understand for a beginner.
Template Section
The template section defines the structure and layout of the component that the user will see.
Detailed Explanation
Overlay Container:
<div class="fixed inset-0 flex items-center justify-center bg-gray-500 bg-opacity-75">
:fixed inset-0
: Makes the container cover the entire screen.flex items-center justify-center
: Centers the content inside the container.bg-gray-500 bg-opacity-75
: Adds a semi-transparent gray background.
Modal Container:
<div class="bg-white p-6 rounded-lg shadow-lg w-96">
:bg-white
: White background.p-6
: Padding inside the container.rounded-lg
: Rounded corners.shadow-lg
: Adds a shadow for a 3D effect.w-96
: Fixed width of the container.
Title:
<h2 class="text-2xl font-bold mb-4">Edit Task</h2>
:text-2xl
: Large text size.font-bold
: Bold text.mb-4
: Margin at the bottom.
Task Input Field:
<input type="text" placeholder="Task" v-model="newTask" class="w-full p-2 border border-gray-300 rounded mb-4" />
:v-model="newTask"
: Binds the input value to thenewTask
variable.w-full
: Full width.p-2
: Padding.border border-gray-300
: Gray border.rounded
: Rounded corners.mb-4
: Margin at the bottom.
Priority Selection:
<div class="mb-4">
: Container for the buttons with bottom margin.<span class="mr-4">Priority:</span>
: Label with right margin.- Buttons:
:class
: Dynamically applies classes based on thepriority
value.@click="priority = 'High'"
: Sets thepriority
to ‘High’ when clicked.
Update Button:
<button @click="handleUpdate" class="w-full bg-blue-500 text-white p-2 rounded">Update</button>
:@click="handleUpdate"
: Calls thehandleUpdate
function when clicked.w-full
: Full width.bg-blue-500
: Blue background.text-white
: White text.p-2
: Padding.rounded
: Rounded corners.
Let’s break down the EditTask.vue
component step by step, explaining each part in a way that’s easy to understand for a beginner.
Script Section
The script section is where we define the data and functions that make the component work.
Script Section Explanation
Imports:
import { ref, defineProps, defineEmits } from 'vue';
: Imports theref
function to create reactive variables,defineProps
to define properties, anddefineEmits
to emit events to the parent component.
Props:
const props = defineProps(['task']);
: Defines the properties that this component expects from its parent. In this case, atask
object.
Emit Function:
const emit = defineEmits(['update-task', 'close']);
: Defines the function to emit events. Here, we define two events:update-task
andclose
.
Reactive Variables:
const newTask = ref(props.task.task);
: Creates a reactive variablenewTask
initialized with thetask
name from theprops
.const priority = ref(props.task.priority);
: Creates a reactive variablepriority
initialized with thepriority
from theprops
.
handleUpdate Function:
const handleUpdate = () => { ... };
: Defines the function to handle updating the task.if (newTask.value && priority.value) { ... }
: Checks if bothnewTask
andpriority
are not empty.const updatedTask = { ...props.task, task: newTask.value, priority: priority.value };
: Creates an updated task object by copying the originaltask
properties and updating thetask
name andpriority
.emit('update-task', updatedTask);
: Emits an event called ‘update-task’ with the updated task object to the parent component.emit('close');
: Emits a ‘close’ event to close the edit task modal.
Summary
This EditTask
component creates a modal for editing an existing task. It includes an input field for the task name and buttons to select the priority (High, Medium, Low). When the “Update” button is clicked, the handleUpdate
function checks if both the new task name and priority are set. If they are, it creates an updated task object, emits an ‘update-task’ event with the updated task object to the parent component, and emits a ‘close’ event to close the modal. The component uses reactive variables to store the new task name and priority and dynamically updates the UI based on these values.
TaskItem Component:
<!-- src/components/TaskItem.vue -->
<template>
<!-- Container for the task item with flex layout, padding, and bottom border -->
<div class="flex items-center justify-between p-4 border-b border-gray-300">
<!-- Container for the task details -->
<div>
<!-- Task title with larger, bold text -->
<h3 class="text-lg font-bold">{{ task.task }}</h3>
<!-- Task priority with dynamic classes based on the priority value -->
<p :class="{
'text-sm': true, // Common class for small text size
'text-red-500': task.priority === 'High', // Red text for high priority
'text-yellow-500': task.priority === 'Medium', // Yellow text for medium priority
'text-green-500': task.priority === 'Low' // Green text for low priority
}">
Priority: {{ task.priority }} // Display the priority value
</p>
</div>
<!-- Container for action buttons -->
<div class="flex items-center">
<!-- Edit button with blue text, emits 'edit-task' event with the task object when clicked -->
<button @click="$emit('edit-task', task)" class="mr-2 text-blue-500">Edit</button>
<!-- Delete button with red text, emits 'delete-task' event with the task ID when clicked -->
<button @click="$emit('delete-task', task.id)" class="text-red-500">Delete</button>
</div>
</div>
</template>
<script setup>
// Define the component props, expecting a `task` object
const props = defineProps(['task']);
</script>
Let’s break down the TaskItem.vue
component step by step, explaining each part in a way that’s easy to understand for a beginner.
Template Section
The template section defines the structure and layout of the component that the user will see.
Detailed Explanation
Container for the Task Item:
<div class="flex items-center justify-between p-4 border-b border-gray-300">
:flex
: Uses flexbox for layout.items-center
: Centers items vertically.justify-between
: Spaces items apart horizontally.p-4
: Adds padding around the content.border-b border-gray-300
: Adds a bottom border with a gray color.
Container for Task Details:
<div>
: Simple container for the task details (title and priority).
Task Title:
<h3 class="text-lg font-bold">{{ task.task }}</h3>
:text-lg
: Large text size.font-bold
: Bold text.{{ task.task }}
: Displays the task’s title from thetask
prop.
Task Priority:
<p :class="{...}">Priority: {{ task.priority }}</p>
::class="{...}"
: Dynamically applies classes based on the task’s priority.text-sm
: Small text size (always applied).text-red-500
: Red text for high priority.text-yellow-500
: Yellow text for medium priority.text-green-500
: Green text for low priority.Priority: {{ task.priority }}
: Displays the task’s priority from thetask
prop.
Container for Action Buttons:
<div class="flex items-center">
:flex
: Uses flexbox for layout.items-center
: Centers items vertically.
Edit Button:
<button @click="$emit('edit-task', task)" class="mr-2 text-blue-500">Edit</button>
:@click="$emit('edit-task', task)"
: Emits anedit-task
event with thetask
object when clicked.mr-2
: Adds a right margin.text-blue-500
: Blue text color.Edit
: Button text.
Delete Button:
<button @click="$emit('delete-task', task.id)" class="text-red-500">Delete</button>
:@click="$emit('delete-task', task.id)"
: Emits adelete-task
event with the task’s ID when clicked.text-red-500
: Red text color.Delete
: Button text.
Let’s break down the TaskItem.vue
component step by step, explaining each part in a way that’s easy to understand for a beginner.
Script Section
The script section is where we define the data and functions that make the component work.
Script Section Explanation
- Props:
const props = defineProps(['task']);
: Defines the properties that this component expects to receive from its parent. In this case, atask
object.
This TaskItem
component displays a task with its title and priority. It also provides “Edit” and “Delete” buttons. When these buttons are clicked, events are emitted to the parent component to handle editing or deleting the task. The task’s priority is styled differently depending on whether it is high, medium, or low.
TaskList Component:
<!-- src/components/TaskList.vue -->
<template>
<!-- Main container with white background, padding, rounded corners, and shadow effect -->
<div class="bg-white p-4 rounded-lg shadow-lg">
<!-- Check if there are tasks available -->
<template v-if="tasks.length > 0">
<!-- Loop through each task and render a TaskItem component -->
<TaskItem
v-for="task in tasks"
:key="task.id"
:task="task"
@edit-task="onEdit"
@delete-task="onDelete"
/>
</template>
<!-- Display a message if there are no tasks available -->
<p v-else class="text-center text-gray-500">No tasks available</p>
</div>
</template>
<script setup>
// Import the TaskItem component
import TaskItem from './TaskItem.vue';
// Define the component props, expecting an array of tasks
const props = defineProps(['tasks']);
// Define the `emit` function to emit events to the parent component
const emit = defineEmits(['edit-task', 'delete-task']);
// Function to handle the edit task event
const onEdit = (task) => {
// Emit the edit task event with the task object
emit('edit-task', task);
};
// Function to handle the delete task event
const onDelete = (taskId) => {
// Emit the delete task event with the task ID
emit('delete-task', taskId);
};
</script>
Let’s break down the TaskList.vue
component step by step, explaining each part in a way that’s easy to understand for a beginner.
Template Section
The template section defines the structure and layout of the component that the user will see.
Detailed Explanation
Main Container:
<div class="bg-white p-4 rounded-lg shadow-lg">
:bg-white
: Sets a white background color.p-4
: Adds padding around the content.rounded-lg
: Rounds the corners of the container.shadow-lg
: Adds a shadow effect to create a 3D look.
Conditional Rendering:
<template v-if="tasks.length > 0">
: Checks if there are tasks available by evaluating the length of thetasks
array.<p v-else class="text-center text-gray-500">No tasks available</p>
: Displays a message if there are no tasks available.
Task Loop:
<TaskItem v-for="task in tasks" :key="task.id" :task="task" @edit-task="onEdit" @delete-task="onDelete" />
:v-for="task in tasks"
: Loops through each task in thetasks
array.:key="task.id"
: Uses the task’sid
as a unique key for eachTaskItem
component.:task="task"
: Passes thetask
object as a prop to theTaskItem
component.@edit-task="onEdit"
: Listens for theedit-task
event and calls theonEdit
function.@delete-task="onDelete"
: Listens for thedelete-task
event and calls theonDelete
function.
Message for No Tasks:
<p v-else class="text-center text-gray-500">No tasks available</p>
:text-center
: Centers the text.text-gray-500
: Sets the text color to gray.
Let’s break down the TaskList.vue
component step by step, explaining each part in a way that’s easy to understand for a beginner.
Script Section
The script section is where we define the data and functions that make the component work.
Script Section Explanation
Imports:
import TaskItem from './TaskItem.vue';
: Imports theTaskItem
component.
Props:
const props = defineProps(['tasks']);
: Defines the properties that this component expects to receive from its parent. In this case, an array oftasks
.
Emit Function:
const emit = defineEmits(['edit-task', 'delete-task']);
: Defines the function to emit events. Here, we define two events:edit-task
anddelete-task
.
Event Handlers:
const onEdit = (task) => { ... };
: Defines the function to handle the edit task event.- Emits an
edit-task
event with thetask
object to the parent component.
- Emits an
const onDelete = (taskId) => { ... };
: Defines the function to handle the delete task event.- Emits a
delete-task
event with the task’sid
to the parent component.
- Emits a
This TaskList
component displays a list of tasks. If there are tasks available, it loops through each task and renders a TaskItem
component for each one. If there are no tasks, it displays a message saying “No tasks available.” The component uses props to receive the list of tasks and emits events to the parent component when a task is edited or deleted. The TaskItem
component is responsible for displaying individual task details and handling edit and delete actions.
Step 3: Implement CRUD Operations in the Home Component
Update the Home.vue
component to handle the state and methods for adding, updating, and deleting tasks.
Home View:
<!-- src/views/Home.vue -->
<template>
<!-- Main container with minimum height screen, gray background, and padding -->
<div class="min-h-screen bg-gray-100 p-4">
<!-- Centered container -->
<div class="container mx-auto">
<!-- Header section with flex layout to space items between -->
<div class="flex justify-between items-center mb-4">
<!-- Title of the task list -->
<h1 class="text-3xl font-bold">Task List</h1>
<!-- Button to open the add task modal -->
<button
@click="isAdding = true"
class="bg-blue-500 text-white px-4 py-2 rounded"
>
+ Add Task
</button>
</div>
<!-- TaskList component, passing tasks as props and handling edit and delete events -->
<TaskList :tasks="tasks" @edit-task="startEditing" @delete-task="deleteTask" />
<!-- AddTask component, shown conditionally if isAdding is true, handling add task and close events -->
<AddTask v-if="isAdding" @add-task="addTask" @close="isAdding = false" />
<!-- EditTask component, shown conditionally if isEditing is true, passing the current task as props and handling update task and close events -->
<EditTask v-if="isEditing" :task="currentTask" @update-task="updateTask" @close="isEditing = false" />
</div>
</div>
</template>
<script setup>
// Import the `ref` function from Vue for creating reactive references
import { ref } from 'vue';
// Import child components
import AddTask from '../components/AddTask.vue';
import EditTask from '../components/EditTask.vue';
import TaskList from '../components/TaskList.vue';
// Define a reactive reference for the list of tasks
const tasks = ref([]);
// Define a reactive reference for the add task modal visibility
const isAdding = ref(false);
// Define a reactive reference for the edit task modal visibility
const isEditing = ref(false);
// Define a reactive reference for the current task being edited
const currentTask = ref(null);
// Function to add a new task to the list
const addTask = (task) => {
// Push the new task to the tasks array
tasks.value.push(task);
// Close the add task modal
isAdding.value = false;
};
// Function to update an existing task in the list
const updateTask = (updatedTask) => {
// Find the index of the task to be updated
const index = tasks.value.findIndex(task => task.id === updatedTask.id);
// If the task exists, update it
if (index !== -1) {
tasks.value[index] = updatedTask;
// Close the edit task modal
isEditing.value = false;
}
};
// Function to delete a task from the list
const deleteTask = (taskId) => {
// Filter out the task with the given ID
tasks.value = tasks.value.filter(task => task.id !== taskId);
};
// Function to start editing a task
const startEditing = (task) => {
// Set the current task to be edited
currentTask.value = task;
// Open the edit task modal
isEditing.value = true;
};
</script>
Let’s break down the Home.vue
component step by step, explaining each part in a way that’s easy to understand for a beginner.
Template Section
The template section defines the structure and layout of the component that the user will see.
Detailed Explanation
Main Container:
<div class="min-h-screen bg-gray-100 p-4">
:min-h-screen
: Ensures the container takes the full height of the screen.bg-gray-100
: Sets a light gray background color.p-4
: Adds padding around the content.
Centered Container:
<div class="container mx-auto">
: Centers the content horizontally.
Header Section:
<div class="flex justify-between items-center mb-4">
:flex
: Uses flexbox for layout.justify-between
: Spaces items apart horizontally.items-center
: Centers items vertically.mb-4
: Adds a margin at the bottom.
Title:
<h1 class="text-3xl font-bold">Task List</h1>
:text-3xl
: Sets a large text size.font-bold
: Makes the text bold.
Add Task Button:
<button @click="isAdding = true" class="bg-blue-500 text-white px-4 py-2 rounded">+ Add Task</button>
:@click="isAdding = true"
: When clicked, setsisAdding
totrue
to show the add task modal.bg-blue-500
: Sets a blue background color.text-white
: Sets white text color.px-4 py-2
: Adds padding on the x (left and right) and y (top and bottom) axes.rounded
: Rounds the corners of the button.
TaskList Component:
<TaskList :tasks="tasks" @edit-task="startEditing" @delete-task="deleteTask" />
::tasks="tasks"
: Passes thetasks
array as a prop to theTaskList
component.@edit-task="startEditing"
: Listens for theedit-task
event and calls thestartEditing
function.@delete-task="deleteTask"
: Listens for thedelete-task
event and calls thedeleteTask
function.
AddTask Component:
<AddTask v-if="isAdding" @add-task="addTask" @close="isAdding = false" />
:v-if="isAdding"
: Conditionally shows theAddTask
component ifisAdding
istrue
.@add-task="addTask"
: Listens for theadd-task
event and calls theaddTask
function.@close="isAdding = false"
: Listens for theclose
event and setsisAdding
tofalse
.
EditTask Component:
<EditTask v-if="isEditing" :task="currentTask" @update-task="updateTask" @close="isEditing = false" />
:v-if="isEditing"
: Conditionally shows theEditTask
component ifisEditing
istrue
.:task="currentTask"
: Passes thecurrentTask
as a prop to theEditTask
component.@update-task="updateTask"
: Listens for theupdate-task
event and calls theupdateTask
function.@close="isEditing = false"
: Listens for theclose
event and setsisEditing
tofalse
.
Let’s break down the Home.vue
component step by step, explaining each part in a way that’s easy to understand for a beginner.
Script Section
The script section is where we define the data and functions that make the component work.
Script Section Explanation
Imports:
import { ref } from 'vue';
: Imports theref
function to create reactive variables.import AddTask from '../components/AddTask.vue';
: Imports theAddTask
component.import EditTask from '../components/EditTask.vue';
: Imports theEditTask
component.import TaskList from '../components/TaskList.vue';
: Imports theTaskList
component.
Reactive Variables:
const tasks = ref([]);
: Creates a reactive variabletasks
initialized as an empty array to store the list of tasks.const isAdding = ref(false);
: Creates a reactive variableisAdding
to control the visibility of the add task modal, initialized tofalse
.const isEditing = ref(false);
: Creates a reactive variableisEditing
to control the visibility of the edit task modal, initialized tofalse
.const currentTask = ref(null);
: Creates a reactive variablecurrentTask
to store the task being edited, initialized tonull
.
Functions:
const addTask = (task) => { ... };
: Defines the function to add a new task to the list.- Adds the new task to the
tasks
array and closes the add task modal.
- Adds the new task to the
const updateTask = (updatedTask) => { ... };
: Defines the function to update an existing task in the list.- Finds the task by its ID, updates it, and closes the edit task modal.
const deleteTask = (taskId) => { ... };
: Defines the function to delete a task from the list.- Removes the task with the given ID from the
tasks
array.
- Removes the task with the given ID from the
const startEditing = (task) => { ... };
: Defines the function to start editing a task.- Sets the
currentTask
to the task being edited and opens the edit task modal.
- Sets the
This Home
component is the main view that displays the task list and allows users to add, edit, and delete tasks. It manages the visibility of the add and edit task modals and handles the corresponding events to update the task list accordingly. The component imports and uses AddTask
, EditTask
, and TaskList
components to handle the specific functionalities related to tasks.
You have successfully implemented the CRUD operations (Create, Read, Update, Delete) for the Task List app. The application now allows users to add, edit, and delete tasks, and the state is managed appropriately. Next, we will move on to Module 4: Deploying the app online.
Module 4: Deploying the App Online
In this module, we will deploy the Task List app online. There are various platforms where you can deploy your Vite + Vue application, such as Vercel, Netlify, GitHub Pages, and more. For simplicity, we will use Vercel, which offers an easy integration with GitHub.
Step 1: Prepare for Deployment
Build the Project: First, we need to build the project for production. Run the following command in your terminal:
npm run build
This will create a
dist
directory with the production build of your app.
Step 2: Deploy to Vercel
Create a Vercel Account: If you don’t already have an account, go to Vercel and sign up.
Install Vercel CLI: Install the Vercel CLI globally on your machine:
npm install -g vercel
Login to Vercel: Login to your Vercel account using the CLI:
vercel login
Initialize the Project: In the root directory of your project, run:
vercel
Follow the prompts to:
- Select the correct scope.
- Link to an existing project or create a new one.
- Select the
dist
directory as the output directory.
Deploy the Project: After initialization, you can deploy your project by running:
vercel --prod
This command will deploy your project to Vercel and give you a URL where your app is live.
Step 3: Continuous Deployment
To set up continuous deployment, follow these steps:
Connect Your GitHub Repository:
- Go to your project dashboard on Vercel.
- Click on “Settings” and then “Git”.
- Connect your GitHub account and select the repository you want to deploy.
Automatic Deployments:
- Every time you push changes to your GitHub repository, Vercel will automatically build and deploy your project.
Welcome to DevTechTutor.com, your ultimate resource for mastering web development and technology! Whether you're a beginner eager to dive into coding or an experienced developer looking to sharpen your skills, DevTechTutor.com is here to guide you every step of the way. Our mission is to make learning web development accessible, engaging, and effective.