Thuta Learning
ProjectsWeb Developmentintermediate

Mini Project: Task Tracker (Part 2 - Adding the Core Feature)

Relax. We'll talk through this in plain words — no textbook voice.

What you'll walk away with

  • Apply Mini Project: Task Tracker (Part 2 - Adding the Core Feature) in a real project
  • Write the code yourself and run it
  • Build out an entire project step by step

Let's think about this for a moment

Building on the structure from Part 1, in Part 2 we'll add the app's core feature: task add/list/toggle functionality. The services, dependency injection, reactive forms, *ngFor directive, and two-way binding lessons are all foundational for this stage. We'll centralize a single TaskService to share data between components, managing the task list with a BehaviorSubject. This keeps component logic simple and reusable.

Let's build it for real

Create `TaskService` with `ng generate service task`, and implement three methods — `getTasks()`, `addTask(task)`, `toggleComplete(id)` — around an internal BehaviorSubject<Task[]>. In TaskListComponent, build a FormGroup (title, description) for adding a new task, and call service.addTask() on submit. Loop over the task list with *ngFor, and wire up each checkbox with [(ngModel)] to toggle completed status. Give completed tasks a strikethrough style with ngClass.

Code Example

typescript
// task.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { Task } from './task.model';

@Injectable({ providedIn: 'root' })
export class TaskService {
  private tasksSubject = new BehaviorSubject<Task[]>([]);
  tasks$ = this.tasksSubject.asObservable();
  private nextId = 1;

  getTasks() {
    return this.tasks$;
  }

  addTask(title: string, description: string) {
    const current = this.tasksSubject.value;
    const newTask: Task = { id: this.nextId++, title, description, completed: false };
    this.tasksSubject.next([...current, newTask]);
  }

  toggleComplete(id: number) {
    const updated = this.tasksSubject.value.map(t =>
      t.id === id ? { ...t, completed: !t.completed } : t
    );
    this.tasksSubject.next(updated);
  }
}
You should see
Typing a task's title/description into the form and submitting it instantly adds a new task to the list, and clicking the checkbox switches it to a strikethrough style.

5-Minute Try-It

Within 5 minutes, add a deleteTask(id) method to TaskService and wire it up to a delete button in TaskListComponent.

A Quick Word of Caution

Change detection will work correctly if you avoid mutating service state directly from within a component, and instead return a new reference via Subject.next().

Easy traps

  • Mutating BehaviorSubject.value directly (using push()) — you should use an immutable pattern (spread operator) instead
  • Using the formGroup directive without importing FormsModule/ReactiveFormsModule for the reactive form group, causing errors

Now Try It Yourself

Within 5 minutes, add a deleteTask(id) method to TaskService and wire it up to a delete button in TaskListComponent.

You'll know it worked when: Typing a task's title/description into the form and submitting it instantly adds a new task to the list, and clicking the checkbox switches it to a strikethrough style.

Mini Project: Task Tracker (Part 2 - Adding the Core Feature) | Thuta Learning