Launch sale! Get 30% OFF expires in 126:46:22

Existing member? Log in and continue learning

See if you like it, start the course for free!

Unlock full course by purchasing a membership
Resource API Refactor
Resource API Refactor
As I mentioned in the introductory modules, the resource
API offers a lot of
power, and a great developer experience, but it also sort of muddies the
boundary between the role of RxJS and signals.
Since these APIs are still marked as experimental in Angular I did not want to rely on using them as the default approach, but it likely will become the default at some point.
In this lesson, we are going to look at how to refactor the application to utilise these APIs.
Refactoring the ChecklistService
We are going to modify ChecklistService
to utilise resource
and
linkedSignal
. We are going to use resource
(via the implementation we
already have in the StorageService
) to handle loading our checklist data.
We will then use linkedSignal
to take that data and create a WritableSignal
that we can then update with our action sources (e.g. add$
, edit$
).
It is a big ask, but if you’re up to the challenge see if you make some progress toward getting this implemented by yourself before continuing. Even just thinking about some of the things that might be changed will be helpful.
Click here to reveal solution
Solution
import { Injectable, effect, inject, linkedSignal,} from '@angular/core';
// sources loadedChecklists = this.storageService.loadedChecklists; add$ = new Subject<AddChecklist>(); edit$ = new Subject<EditChecklist>(); remove$ = this.checklistItemService.checklistRemoved$;
// state checklists = linkedSignal({ source: this.loadedChecklists.value, computation: (checklists) => checklists ?? [], });
constructor() { this.add$ .pipe(takeUntilDestroyed()) .subscribe((checklist) => this.checklists.update((checklists) => [ ...checklists, this.addIdToChecklist(checklist), ]), );
this.remove$ .pipe(takeUntilDestroyed()) .subscribe((id) => this.checklists.update((checklists) => checklists.filter((checklist) => checklist.id !== id), ), );
this.edit$ .pipe(takeUntilDestroyed()) .subscribe((update) => this.checklists.update((checklists) => checklists.map((checklist) => checklist.id === update.id ? { ...checklist, title: update.data.title } : checklist, ), ), );
// effects effect(() => { const checklists = this.checklists(); if (this.loadedChecklists.status() === 'resolved') { this.storageService.saveChecklists(checklists); } }); }
There are a few differences here, so let’s talk through them.
The biggest difference is that we have replaced our checklistsLoaded$
source
with loadedChecklists
which contains the result from the resource
API. This
means that we no longer have the subscribe
to checklistsLoaded$
in the
constructor and we don’t need to worry about manually handling the loaded
and error
state so we are able to remove those as well.
This is a pretty big win, this is all of the boilerplate we are able to just instantly get rid of:
export interface ChecklistsState { checklists: Checklist[]; loaded: boolean; error: string | null;}
private state = signal<ChecklistsState>({ checklists: [], loaded: false, error: null,});
checklists = computed(() => this.state().checklists);loaded = computed(() => this.state().loaded);error = computed(() => this.state().error);
this.checklistsLoaded$.pipe(takeUntilDestroyed()).subscribe({ next: (checklists) => this.state.update((state) => ({ ...state, checklists, loaded: true, })), error: (err) => this.state.update((state) => ({ ...state, error: err })),});
Instead of all of that we just have:
loadedChecklists = this.storageService.loadedChecklists();
Pretty cool, right? But we’ve also added this:
checklists = linkedSignal({ source: this.loadedChecklists.value, computation: (checklists) => checklists ?? [], });
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).