Editing and Deleting Data
The last key feature we need to focus on is the ability to edit and delete the checklists and checklist items. This will involve a reasonably simple extension in terms of the functionality in our services.
Update the Services
We will start by adding support for editing and deleting items in the
ChecklistItemService
. This is by no means easy or obvious, but this will serve
well as an opportunity to implement a slightly more advanced feature with no
guidance.
Before continuing, see if you can add new sources and reducers that allow
deleting and editing specific checklist items. We have already created the
appropriate types for EditChecklistItem
and RemoveChecklistItem
so you
can use these as reference for the type of data you will be working with.
See how much progress you can make on this and then take a look at my solution below (don’t worry if you get stuck).
Click here to reveal solution
Solution
Add the following sources to
ChecklistItemService
:
remove$ = new Subject<RemoveChecklistItem>();
edit$ = new Subject<EditChecklistItem>();
Add the following reducers to
ChecklistItemService
:
this.edit$.pipe(takeUntilDestroyed()).subscribe((update) =>
this.state.update((state) => ({
...state,
checklistItems: state.checklistItems.map((item) =>
item.id === update.id ? { ...item, title: update.data.title } : item
),
}))
);
this.remove$.pipe(takeUntilDestroyed()).subscribe((id) =>
this.state.update((state) => ({
...state,
checklistItems: state.checklistItems.filter((item) => item.id !== id),
}))
);
That’s all we need to do — we can now next
the edit$
and remove$
sources
to edit or remove checklist items.
Now we want to handle removing and editing checklists in the
ChecklistService
… but we have a bit of a surprise in store first.
Consider that if we want to remove a checklist, we will also want to remove
all of the checklist items for that checklist. That means that our
ChecklistItemService
is going to need to know when the removal of a checklist
is triggered, so that it can handle removing all of the items related to that
checklist.
To handle this, we are going to create a shared source. We are actually
going to add a checklistRemoved$
source to our ChecklistItemService
. Our
ChecklistItemService
will react to that by removing all of the items. Then, in
our ChecklistService
we will use the same checklistRemoved$
source from
ChecklistItemService
and the ChecklistService
will react to that same source
emitting by removing the appropriate checklist. When we trigger the
checklistRemoved$
source in the ChecklistItemService
both our
ChecklistItemService
and our ChecklistService
will automatically react
(just in different ways).
Add the following source to the
ChecklistItemService
:
checklistRemoved$ = new Subject<RemoveChecklist>();
Add the following reducer to the
ChecklistItemService
:
this.checklistRemoved$.pipe(takeUntilDestroyed()).subscribe((checklistId) =>
this.state.update((state) => ({
...state,
checklistItems: state.checklistItems.filter(
(item) => item.checklistId !== checklistId
),
}))
);
Now when the checklistRemoved$
source emits we will remove all of the items
for that checklistId
.
Now we will utilise this same checklistRemoved$
source in our
ChecklistService
.
Add the following source to the
ChecklistService
:
remove$ = this.checklistItemService.checklistRemoved$;
Now we inject
the ChecklistItemService
and utilise the same source for the
removal in the ChecklistService
. This is better than having two separate
sources in each service, because we would need to make sure we trigger the
sources in both services whenever we want to remove a checklist. This way, we
can just trigger the one shared source.
We also need a source to handle editing in the ChecklistService
as well.
Add the following source to the
ChecklistService
:
edit$ = new Subject<EditChecklist>();
Add the following reducers to the
ChecklistService
:
this.remove$.pipe(takeUntilDestroyed()).subscribe((id) =>
this.state.update((state) => ({
...state,
checklists: state.checklists.filter((checklist) => checklist.id !== id),
}))
);
this.edit$.pipe(takeUntilDestroyed()).subscribe((update) =>
this.state.update((state) => ({
...state,
checklists: state.checklists.map((checklist) =>
checklist.id === update.id
? { ...checklist, title: update.data.title }
: checklist
),
}))
);