DEV Community

Cover image for Building a Simple CRUD Task Manager App with Angular: A Step-by-Step Guide for Beginners
Ayush Agarwal
Ayush Agarwal

Posted on • Edited on • Originally published at Medium

Building a Simple CRUD Task Manager App with Angular: A Step-by-Step Guide for Beginners

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:

  1. Node.js: Ensure you have Node.js installed, including npm (Node Package Manager).

  2. Angular CLI: Install Angular CLI globally using npm by running the following command in your terminal:

npm install -g @angular/cli
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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.tsfile 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 { }
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

We’ve created a simple form layout using Tailwind CSS classes. The form includes an input field bound to the newTaskproperty 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 = '';
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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[] = [];
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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 { }
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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 completedproperty 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;
  }
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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;
  }
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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:

  1. 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.

  2. 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.

  3. Task Due Dates and Reminders: Enable users to set due dates for tasks and receive reminders or notifications to complete tasks on time.

  4. Task Notes and Attachments: Allow users to add notes or attach files to tasks, providing additional context or resources related to each task.

  5. User Accounts and Data Persistence: Implement user authentication and data persistence, so users can save their tasks across sessions and access them securely.

  6. 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.

  7. Task Sharing and Collaboration: Introduce task-sharing capabilities, allowing users to collaborate on tasks with others and assign tasks to team members.

  8. Dark Mode: Offer a dark mode option to enhance user experience and reduce eye strain, especially during late-night usage.

  9. Task Analytics and Insights: Provide users with visual analytics and insights on task completion rates, trends, and productivity statistics.

  10. 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.

Buy me a COFFEE!

Thank you! 🙏

Happy coding, and keep exploring the exciting possibilities of Angular development!

Top comments (0)