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. The first thing we are going to do is create the service itself.

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

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { Checklist } from '../interfaces/checklist';

@Injectable({
  providedIn: 'root',
})
export class ChecklistService {
  private checklists$ = new BehaviorSubject<Checklist[]>([]);

  getChecklists() {
    return this.checklists$.asObservable();
  }

  add(checklist: Pick<Checklist, 'title'>) {
    const newChecklist = {
      ...checklist,
      id: this.generateSlug(checklist.title),
    };

    this.checklists$.next([...this.checklists$.value, newChecklist]);
  }

  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$.value.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;
  }
}

This is quite similar to some stuff we have seen before, especially in relation to how we are using a BehaviorSubject. Specifically, we create this stream:

private checklists$ = new BehaviorSubject<Checklist[]>([]);

Which will allow us to emit new checklist values by triggering its next method, and we expose this stream as a simple observable to the rest of the application:

  getChecklists() {
    return this.checklists$.asObservable();
  }

This way, other parts of the application will not be able to trigger the next method on the stream. Since our BehaviorSubject is private only code within the service call will be able to access it to emit new values.

There are also some strange things going on here that we haven’t seen before. Like this:

  add(checklist: Pick<Checklist, 'title'>) {
    const newChecklist = {
      ...checklist,
      id: this.generateSlug(checklist.title),
    };

    this.checklists$.next([...this.checklists$.value, newChecklist]);
  }

This is similar to what we did in the todo application. We pass in a checklist, and then we use the spread syntax to emit this value:

[...this.checklists$.value, newChecklist]

This gets the array of all current checklists in the stream, and spreads them out into a new array, and then we add out newChecklist onto the end of all of those. The result is a new single array that contains all of the old checklists and our new one. But the strangest thing about this method is perhaps this:

add(checklist: Pick<Checklist, 'title'>)

We are using the TypeScript Pick utility type. This isn’t strictly needed, but it can make our lives easier. What Pick allows us to do is take an existing type, Checklist in this case, and pick the properties we want to apply from that type. If you remember, a Checklist is made up of two properties:

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

But, we don’t want to supply the unique id ourselves when we create the checklist - we want this to be auto generated. So, we use Pick to modify this type to only include the title property.

Then, we trigger this method to generate the id for us:

  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$.value.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).