Creating Checklist Items
The path we take to implement this feature is going to look pretty similar to creating the checklists, and it will be the same with just about any feature we would implement.
Create the ChecklistItem Interface
We are creating a new entity in our application now — items that belong to a specific checklist. We are first going to define a new type that represents this entity in the application.
Create a file at
src/app/shared/interfaces/checklist-item.ts
and add the following:
import { RemoveChecklist } from './checklist';
export interface ChecklistItem {
id: string;
checklistId: string;
title: string;
checked: boolean;
}
export type AddChecklistItem = {
item: Omit<ChecklistItem, 'id' | 'checklistId' | 'checked'>;
checklistId: RemoveChecklist;
};
export type EditChecklistItem = {
id: ChecklistItem['id'];
data: AddChecklistItem['item'];
};
export type RemoveChecklistItem = ChecklistItem['id'];
In general, this is the same idea as our Checklist
type but there is some
extra weirdness happening here. First of all, our ChecklistItem
type is just
different — as well as the title
we also have a checklistId
to indicate what
Checklist
it belongs to, and a checked
to indicated whether it is currently
in the completed state or not.
We have the same style of types for adding and editing as we did with the
Checklist
but for this one we also have our checklistId
property based on
whatever the RemoveChecklist
type is from our Checklist
interfaces.
We could just give checklistId
a type of string
which would match the id
from our Checklist
interface. This is just a bit safer because if we ever
update the type of the id
in Checklist
we won’t need to remember to change
this too.
Creating the Checklist Item service
Our checklist service was created in the shared/data-access
folder because it
is used by multiple features in the application. However, our
ChecklistItemService
will only be used by the checklist item feature — so, we
will store it inside of the checklist/data-access
folder.
Create a new service at
src/app/checklist/data-access/checklist-item.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 {
AddChecklistItem,
ChecklistItem,
} from '../../shared/interfaces/checklist-item';
export interface ChecklistItemsState {
checklistItems: ChecklistItem[];
}
@Injectable({
providedIn: 'root',
})
export class ChecklistItemService {
// state
private state = signal<ChecklistItemsState>({
checklistItems: [],
});
// selectors
checklistItems = computed(() => this.state().checklistItems);
// sources
add$ = new Subject<AddChecklistItem>();
constructor() {
this.add$.pipe(takeUntilDestroyed()).subscribe((checklistItem) =>
this.state.update((state) => ({
...state,
checklistItems: [
...state.checklistItems,
{
...checklistItem.item,
id: Date.now().toString(),
checklistId: checklistItem.checklistId,
checked: false,
},
],
}))
);
}
}
Again, nothing really new here — this is almost identical to what we did with
the ChecklistService
. Our reducer is a bit more complicated so let’s talk
through what is happening here:
this.state.update((state) => ({
...state,
checklistItems: [
...state.checklistItems,
{
...checklistItem.item,
id: Date.now().toString(),
checklistId: checklistItem.checklistId,
checked: false,
},
],
}))
We are trying to update the checklistItems
state. This service will contain an
array of all items for all checklists. We add a checklistId
to each
item to tell which checklist it belongs to.
When updating our state, we first copy all of the existing checklistItems
into
the new array:
...state.checklistItems,