3d cartoon hands holding a phone

Unlock full course by purchasing a membership

Lesson 6

Creating a Checklist Service

Handling checklist data

STANDARD

Creating a Checklist Service

In this lesson, we are going to take the data entered into our form and save it using a service. This will be our first chance to put those state management concepts we have been talking about into practice.

The first thing we are going to do is add a few more types.

Modify src/app/shared/interfaces/checklist.ts to reflect the following:

export interface Checklist {
  id: string;
  title: string;
}

export type AddChecklist = Omit<Checklist, 'id'>;
export type EditChecklist = { id: Checklist['id']; data: AddChecklist };
export type RemoveChecklist = Checklist['id'];

We have added three types here: AddChecklist, EditChecklist, and RemoveChecklist. We are going to use different types of data for different actions.

When we want to add a checklist we will only supply the title as the id is generated automatically. To handle this we create a new type using the Omit utility type that allows us to remove a particular property from an existing type.

When we edit a checklist we will supply the action in this format:

{
  id: 'id of checklist being edited',
  data: { /* data we want to update */ }
}

To do this, we create a new type using Checklist['id'] which will become whatever the type of the id property is in the Checklist type. We do a similar thing for our remove type too where we will only need to supply the id of the checklist we are removing.

Now let’s move on to creating the service.

Create a file at src/app/shared/data-access/checklist.service.ts and add the following:

import { Injectable, computed, signal } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { Subject } from 'rxjs';
import { AddChecklist, Checklist } from '../interfaces/checklist';

export interface ChecklistsState {
  checklists: Checklist[];
}

@Injectable({
  providedIn: 'root',
})
export class ChecklistService {
  // state
  private state = signal<ChecklistsState>({
    checklists: [],
  });

  // selectors
  checklists = computed(() => this.state().checklists);

  // sources
  add$ = new Subject<AddChecklist>();

  constructor() {
    // reducers
    this.add$.pipe(takeUntilDestroyed()).subscribe((checklist) =>
      this.state.update((state) => ({
        ...state,
        checklists: [...state.checklists, this.addIdToChecklist(checklist)],
      }))
    );
  }

  private addIdToChecklist(checklist: AddChecklist) {
    return {
      ...checklist,
      id: this.generateSlug(checklist.title),
    };
  }

  private generateSlug(title: string) {
    // NOTE: This is a simplistic slug generator and will not handle things like special characters.
    let slug = title.toLowerCase().replace(/\s+/g, '-');

    // Check if the slug already exists
    const matchingSlugs = this.checklists().find(
      (checklist) => checklist.id === slug
    );

    // If the title is already being used, add a string to make the slug unique
    if (matchingSlugs) {
      slug = slug + Date.now().toString();
    }

    return slug;
  }
}
STANDARD
Key

Thanks for checking out the preview of this lesson!

You do not have the appropriate membership to view the full lesson. If you would like full access to this module you can view membership options (or log in if you are already have an appropriate membership).