Building a Simple CRUD Task Manager App with Angular: A Step-by-Step Guide for Beginners
Tags: angular, typescript, javascript, tailwindcss
Published: 2023-07-20 14:11:50

### 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](https://tasks.ayushdev.com/), where you can interact with the app and get a feel for its functionalities.

[Github Repo](https://github.com/ayushhagarwal/todo_angular_application) of the project.

Project Demo: 

{% embed https://vimeo.com/846939949?share=copy %}

#### Prerequisites

Before we get started, make sure you have the following installed on your machine:

1. [Node.js](https://nodejs.org/en/): 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:
    

```typescript
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](https://blogs.ayushdev.com/how-to-integrate-tailwind-css-in-your-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:

```typescript
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.

```typescript
<!-- 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.

```typescript
// 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:

```typescript
<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:

```bash
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.

```html
<!-- 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.

```typescript
// 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.

```html
<!-- 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.

```bash
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.

```xml
<!-- 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,

```typescript
// 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.

```xml
<!-- 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`:

```typescript
// 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.

```typescript
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.

```typescript
// 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.

```typescript
// 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.

```html
<!-- 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.

```typescript
// 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.

```html
<!-- 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:

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!**](https://www.buymeacoffee.com/ayushdev)

Thank you! 🙏

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