Introduction
Welcome to a simple guide on building a CRUD (Create, Read, Update, Delete) Task Manager app using Angular! In this blog, we’ll walk you through creating a functional task management application that allows users to add, view, complete, and delete tasks. Whether you’re new to Angular or just looking to reinforce your skills, this tutorial will help you understand the basics of Angular, Tailwind CSS, and how they work together to create a sleek and efficient task manager.
Live View
Before we dive into the development process, feel free to check out the live view of the task manager app we’ll be building. Here’s the link to the live view, where you can interact with the app and get a feel for its functionalities.
Github Repo of the project.
Project Demo:
Prerequisites
Before we get started, make sure you have the following installed on your machine:
Node.js: Ensure you have Node.js installed, including npm (Node Package Manager).
Angular CLI: Install Angular CLI globally using npm by running the following command in your terminal:
npm install -g @angular/cli
Setting Up the Angular Project with Tailwind CSS
Setting up an Angular project with Tailwind CSS is an essential step in the development process. If you need a detailed guide on how to set up Tailwind CSS in your Angular project, refer to my blog post Integrating Tailwind CSS in Angular Project.
Following the instructions in the blog post will grate Tailwind CSS into your Angular project, enabling you to create stunning UI components.
Now that you’ve set up your Angular project with Tailwind CSS let’s build our task manager components and functionalities!
Creating the Navbar Component
In this section, we will create the Navbar. It will display the app title and any additional navigation links in the future.
Step 1: Generating the Navbar Component
To start, create a components folder, then use the Angular CLI to generate the Navbar component. Open your terminal or command prompt and run the following command:
ng generate component components/navbar
This command will generate a new “navbar component” inside your Angular project’s “components” folder. The Angular CLI will also create the necessary files and configurations for the component.
Step 2: Implementing the Navbar Template
Now that we have the Navbar component generated let’s add the HTML code to create the Navbar layout. Open the navbar.component.html
file in the components/navbar folder and add the code below.
<!-- components/navbar/navbar.component.html -->
<nav class="flex items-center justify-between flex-wrap bg-indigo-400 p-4">
<div class="flex items-center flex-shrink-0 text-white mr-6">
<span class="font-semibold text-xl tracking-tight">Tasks</span>
</div>
</nav>
In the above code, we’ve created a simple Navbar with a blue background color (bg-indigo-400
) and white text (text-white
). The Navbar contains the title "Tasks" which is the name of our task manager app. You can customize the styles and content of the Navbar as per your project requirements.
Step 3: Registering the Navbar Component
Next, we need to register the Navbar component in the Angular app to use it in other parts of the application. The angular CLI would have done this automatically when you generated the component. If not, open the app.module.ts
file in the src/app
folder.
In the app.module.ts
file, import the NavbarComponent
and add it to the declarations
array.
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { NavbarComponent } from './components/navbar/navbar.component'; // Import the NavbarComponent
@NgModule({
declarations: [
AppComponent,
NavbarComponent // Add the NavbarComponent to the declarations array
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Step 4: Using the Navbar Component
With the Navbar component registered, we can now use it in the main app component (app.component.html
). Open the app.component.html
file located in the src/app
folder.
Replace the existing content in the app.component.html
file with the following code:
<app-navbar></app-navbar>
The above code uses the <app-navbar></app-navbar>
selector to include the Navbar component within the main app component. When you run the app, you should see the Navbar displayed at the top of the page titled "Tasks."
Congratulations! You’ve successfully created and integrated the Navbar component into your Angular app. In the following sections, we’ll build the app's task creation form and tasks. Keep following along to develop your task manager app from scratch!
Building the Task Creation Form
In this section, we will build the Task Creation Form component, which allows users to add new tasks to the task manager app. The form will include an input field for entering the task name and a button to submit the task.
Step 1: Generating the Task Creation Form Component
Let’s use the Angular CLI to generate the Task Creation Form component. Open your terminal or command prompt and run the following command:
ng generate component components/task-form
This command will generate a new component named “task-form”.
Step 2: Implementing the Task Creation Form Template
Open the task-form.component.html
file located in the components/task-form
folder.
Type in the code mentioned below.
<!-- components/task-form/task-form.component.html -->
<div class="container mx-auto py-10">
<form class="w-full max-w-sm">
<div class="flex items-center border-b-2 border-teal-500 py-2">
<input
[(ngModel)]="newTask"
name="newTask"
class="appearance-none bg-transparent border-none w-full text-gray-700 mr-3 py-1 px-2 leading-tight focus:outline-none"
type="text"
placeholder="Add task"
/>
<button
type="button"
(click)="addTask()"
class="flex-shrink-0 bg-teal-500 hover:bg-teal-700 border-teal-500 hover:border-teal-700 text-sm border-4 text-white py-1 px-2 rounded"
>
Add
</button>
</div>
</form>
</div>
We’ve created a simple form layout using Tailwind CSS classes. The form includes an input field bound to the newTask
property using Angular's two-way data binding [(ngModel)]
. Users entering an input field will automatically update the newTask
property in the component.
The form also has a “Add” button with a click event (click)="addTask()"
. When the user clicks the "Add" button, the addTask()
method in the component will be called to add the new task to the tasks
array.
Step 3: Implementing the Task Creation Form Component
Now let’s update the task-form.component.ts
file located in the components/task-form
folder to implement the functionality of adding tasks.
// components/task-form/task-form.component.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-task-form',
templateUrl: './task-form.component.html'
})
export class TaskFormComponent implements OnInit {
tasks: string[] = [];
newTask: string = '';
constructor() { }
ngOnInit(): void {
}
addTask() {
if (this.newTask.trim() !== '') {
this.tasks.push(this.newTask);
this.newTask = '';
}
}
}
In the component class, we have the tasks
array to store all the tasks and the newTask
property to hold the new task entered in the input field.
The addTask()
method is called when clicking the "Add" button. It first checks if the newTask
is not empty (by using .trim() !== ''
). If the task name is not empty, it adds the new task to the tasks
array using this.tasks.push(this.newTask)
. Finally, it resets the newTask
property to an empty string to clear the input field.
Step 4: Using the Task Creation Form Component
Now that we’ve implemented the Task Creation Form component let’s use it in the main app component (app.component.html
). Open the app.component.html
file located in the src/app
folder.
<!-- src/app/app.component.html -->
<app-navbar></app-navbar>
<app-task-form></app-task-form>
You will see the form to add the task upon saving.
In the next section, we’ll build the Task Display component to show the list of tasks.
Task List: Displaying the List of Tasks
In this section, we will create the Task List component to display the list of tasks added by users. The Task List component will receive the tasks array as input from the Task Form component and show each task in a simple card-like format.
Generating the Task List Component
Let’s use the Angular CLI to generate the Task List component.
ng generate component components/tasks-list
This command will generate a new “tasks-list” component inside your Angular project's “components” folder.
Passing Tasks from Task Form to Task List
In the task-form.component.html
file, we must pass the tasks
array to the Task List component using the [tasks]
property binding.
<!-- components/task-form/task-form.component.html -->
<div class="container mx-auto py-10">
<form class="w-full max-w-sm">
<div class="flex items-center border-b-2 border-teal-500 py-2">
<input
[(ngModel)]="newTask"
name="newTask"
class="appearance-none bg-transparent border-none w-full text-gray-700 mr-3 py-1 px-2 leading-tight focus:outline-none"
type="text"
placeholder="Add task"
/>
<button
type="button"
(click)="addTask()"
class="flex-shrink-0 bg-teal-500 hover:bg-teal-700 border-teal-500 hover:border-teal-700 text-sm border-4 text-white py-1 px-2 rounded"
>
Add
</button>
</div>
</form>
</div>
<div>
<app-tasks-list [tasks]="tasks"></app-tasks-list>
</div>
In the above code, we pass the tasks
array from the Task Form component to the Task List component using property binding [tasks]="tasks"
. This ensures that the Task List component receives and can access the tasks
array.
To receive the tasks
array in the Task List component, we must define an @Input()
property named tasks
in the component's TypeScript file. Open the tasks-list.component.ts
file,
// components/tasks-list/tasks-list.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-tasks-list',
templateUrl: './tasks-list.component.html'
})
export class TasksListComponent {
@Input() tasks: string[] = [];
}
In the above code, we’ve defined an @Input()
property named tasks
of type string[]
in the Task List component. This property will receive the tasks array from the parent component (Task Form component).
Implementing the Task List Template
Open the tasks-list.component.html
file located in the components/tasks-list
folder.
<!-- components/tasks-list/tasks-list.component.html -->
<div *ngIf="tasks.length > 0" class="container mx-auto py-4">
<div *ngFor="let task of tasks" class="w-1/4 bg-gray-100 p-4 mb-4 rounded-lg">
<ul>
<li class="text-gray-800">{{ task }}</li>
</ul>
</div>
</div>
In the above code, we’re using Angular’s *ngIf
directive to check for any tasks in the tasks
array. If there are tasks, the *ngIf
condition evaluates to true
, and the list of tasks will be displayed.
We use the *ngFor
directive to loop through each task in the tasks
array and display it within a rounded card-like container (bg-gray-100 p-4 mb-4 rounded-lg
). The task name is displayed using the {{ task }}
interpolation syntax.
BONUS CONTENT :
When using one component inside another component’s template, it’s crucial to ensure that the component being used is imported and declared before using it. This way, Angular knows the component's existence and can render it correctly.
Another way to do this would be to use modules at the component level and import these modules in app.module.ts.
Here’s the updated code for the app.module.ts
file, ensuring that the TasksListComponent
is imported before the TaskFormComponent
:
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { TasksListComponent } from './components/tasks-list/tasks-list.component'; // Import Task List component
import { TaskFormComponent } from './components/task-form/task-form.component'; // Import Task Form component
@NgModule({
declarations: [
AppComponent,
TasksListComponent, // Add Task List component to the declarations array before Task Form component
TaskFormComponent // Add Task Form component to the declarations array
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
With this change, the Task List component will be imported and available before the Task Form component. This will prevent any errors related to components not found during the app’s compilation process.
Completing Tasks: Marking Tasks as Completed
This section will implement the functionality to mark tasks as completed in the Task List component. We’ll introduce a new Task
interface to represent each task with a name
and completed
property. The completed
property will track whether a task is marked as completed or not.
This will be some redundant work, and we could have done the interface logic from the beginning, but this blog's purpose is to help you understand how the project is enhanced and built while introducing new functionality.
Step 1: Creating the Task Interface
To represent each task, we’ll create a new interface named Task
. This interface will be defined inside the tasks.interface.ts
file in the src/app/constants
folder.
It is a good practice to have interfaces or enums in the constants folder for a better code structure.
export interface Task {
name: string;
completed: boolean;
}
The Task
interface has two properties:
name
: represents the name of the task (a string).completed
: represents whether the task is completed or not (a boolean).
Step 2: Modifying Task Form Component
In the task-form.component.ts
file, we need to modify the component to use the Task
interface for tasks and update the addTask()
method accordingly.
// components/task-form/task-form.component.ts
import { Component, OnInit } from '@angular/core';
import { Task } from '../../constants/tasks.interface';
@Component({
selector: 'app-tasks-form',
templateUrl: './tasks-form.component.html'
})
export class TasksFormComponent implements OnInit {
tasks: Task[] = [];
newTask: string = '';
constructor() { }
ngOnInit(): void {
}
addTask() {
if (this.newTask.trim() !== '') {
const newTask: Task = {
name: this.newTask,
completed: false
};
this.tasks.push(newTask);
this.newTask = '';
console.log(this.tasks);
}
}
}
In the updated code, we’ve imported the Task
interface and changed the tasks
array to be of type Task[]
. When adding a new task using the addTask()
method, we create a new Task
object with the provided task name and set the completed
property to false
.
Step 3: Modifying Task List Component
Next, we need to modify the task-list.component.ts
file to use the Task
interface and implement the toggleCompleted()
method to mark tasks as completed.
// components/tasks-list/tasks-list.component.ts
import { Component, Input, OnInit } from '@angular/core';
import { Task } from '../../constants/tasks.interface';
@Component({
selector: 'app-tasks-list',
templateUrl: './tasks-list.component.html',
})
export class TasksListComponent implements OnInit {
@Input() tasks: Task[];
constructor() { }
ngOnInit(): void {
}
removeTask(task: Task) {
const taskIndex = this.tasks.indexOf(task);
if (taskIndex !== -1) {
this.tasks.splice(taskIndex, 1);
}
}
toggleCompleted(task: Task) {
task.completed = !task.completed;
}
}
In the updated code, we’ve imported the Task
interface and changed the tasks
array to be of type Task[]
. The toggleCompleted()
method toggles the completed
property of the task to mark it as completed or not completed based on its current state.
Step 4: Updating Task List Template
Finally, we need to update the task-list.component.html
file to display the task name and add an icon to toggle the task's completion status.
<!-- components/tasks-list/tasks-list.component.html -->
<div *ngIf="tasks.length > 0" class="container mx-auto py-4">
<div *ngFor="let task of tasks" class="bg-gray-100 p-4 mb-4 rounded-lg">
<ul>
<li
[class.line-through]="task.completed"
class="flex items-center justify-between text-gray-800"
>
{{ task.name }}
<div class="flex items-center justify-end">
<span (click)="toggleCompleted(task)" style="cursor: pointer">
<svg
class="h-8 w-8 text-green-500"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
</span>
</div>
</li>
</ul>
</div>
</div>
In the updated template, we use the [class.line-through]
property binding to apply a strikethrough style to the task name if marked as completed (task.completed
is true
). The <svg>
icon represents a checkmark icon, and when clicked, it calls the toggleCompleted()
method to change the completion status of the corresponding task.
With this done, users can now toggle the completion status of tasks, making your app more interactive and useful. In the next section, we’ll implement the functionality to remove tasks from the list.
Removing Tasks: Deleting Tasks from the List
This section will implement the functionality to remove tasks from the task list. When users click the “remove” icon next to a task, that specific task will be deleted from the list.
Step 1: Implementing the Remove Task Function
In the tasks-list.component.ts
file, we define a function removeTask(task: Task)
function to handle task removal.
// components/tasks-list/tasks-list.component.ts
import { Component, Input, OnInit } from '@angular/core';
import { Task } from '../../constants/tasks.interface';
@Component({
selector: 'app-tasks-list',
templateUrl: './tasks-list.component.html',
})
export class TasksListComponent implements OnInit {
@Input() tasks: Task[];
constructor() { }
ngOnInit(): void {
}
removeTask(task: Task) {
const taskIndex = this.tasks.indexOf(task);
if (taskIndex !== -1) {
this.tasks.splice(taskIndex, 1);
}
}
toggleCompleted(task: Task) {
task.completed = !task.completed;
}
}
In the removeTask(task: Task)
function, we receive the task
object that needs to be removed from the tasks
array. We find the index of this task using this.tasks.indexOf(task)
and check if it exists (taskIndex !== -1
). If the task exists in the array, we use splice()
to remove it from the array.
Step 2: Updating Task List Template
Now, let’s update the task-list.component.html
template to include the "remove" icon for each task and call the removeTask()
function when the icon is clicked.
<!-- components/tasks-list/tasks-list.component.html -->
<div *ngIf="tasks.length > 0" class="container mx-auto py-4">
<div *ngFor="let task of tasks" class="bg-gray-100 p-4 mb-4 rounded-lg">
<ul>
<li
[class.line-through]="task.completed"
class="flex items-center justify-between text-gray-800"
>
{{ task.name }}
<div class="flex items-center justify-end">
<span (click)="removeTask(task)" style="cursor: pointer">
<svg
class="h-8 w-8 text-red-500"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
</span>
<span (click)="toggleCompleted(task)" style="cursor: pointer">
<svg
class="h-8 w-8 text-green-500"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
</span>
</div>
</li>
</ul>
</div>
</div>
In the updated template, we’ve added a “remove” icon represented by the <svg>
element. When users click on this icon, it calls the removeTask(task)
function with the corresponding task
as an argument, effectively removing the task from the list.
Users can delete tasks as needed, making your app more dynamic and user-friendly.
That’s it. this, your super simple task manager application is complete now. Let’s see some possible ideas for enhancements to this application.
Future Enhancements: Taking Your Task Manager App to the Next Level
While you’ve successfully created a basic task manager app, there’s always room for improvement and additional features. Here are some ideas for future enhancements to make your app even more useful and user-friendly:
Task Categories and Filtering: Allow users to categorize tasks and provide filtering options to view tasks based on their categories. This can help users organize and manage their tasks more effectively.
Task Prioritization: Add the ability to set task priorities, such as high, medium, and low. Users can then sort tasks based on their priority levels.
Task Due Dates and Reminders: Enable users to set due dates for tasks and receive reminders or notifications to complete tasks on time.
Task Notes and Attachments: Allow users to add notes or attach files to tasks, providing additional context or resources related to each task.
User Accounts and Data Persistence: Implement user authentication and data persistence, so users can save their tasks across sessions and access them securely.
Drag and Drop Reordering: Implement a drag-and-drop feature to reorder tasks in the list, giving users greater control over the arrangement of tasks.
Task Sharing and Collaboration: Introduce task-sharing capabilities, allowing users to collaborate on tasks with others and assign tasks to team members.
Dark Mode: Offer a dark mode option to enhance user experience and reduce eye strain, especially during late-night usage.
Task Analytics and Insights: Provide users with visual analytics and insights on task completion rates, trends, and productivity statistics.
Mobile App Version: Develop a mobile app version of your task manager to extend its usability and reach to mobile device users.
Continuous improvement and innovation will keep your task manager app relevant and valuable to users.
Conclusion: Building a Feature-Packed Task Manager
This tutorial taught you how to create a task manager app from scratch using Angular. Starting with setting up the project with Tailwind CSS, you built the Navbar component, the task creation form, and the task display using the Task List component. You then added functionality to mark tasks as completed and remove tasks from the list.
With the flexibility of Angular and the power of Tailwind CSS, you have the foundation to expand further and customize your task manager app.
Congratulations on completing your task manager app!
Lastly, Your support keeps me going, and I give my 100 percent to these blogs! If you've found value, consider fueling the blog with a coffee ☕️ donation at below link.
Thank you! 🙏
Happy coding, and keep exploring the exciting possibilities of Angular development!
Top comments (0)