I have been defining resource as follows:
resource: ResourceRef<any> = resource<ResourceRequest | undefined, any>({
request: (): ResourceRequest | undefined => {
return {
id: this.id(),
};
},
loader: ({ request: { id }, abortSignal }) => {
return fetch(`https://jsonplaceholder.typicode.com/todos/${id}`).then(
(res: any) => res.json()
);
},
});
Seems sufficient to achieve what I want, but I see this pattern in blogs and youtube where they add the AbortSignal
to the fetch request.
loader: ({ request: { id }, abortSignal }) => {
return fetch(`https://jsonplaceholder.typicode.com/todos/${id}`, {
signal: abortSignal,
}).then((res: any) => res.json());
},
What is the necessity for this?
Below is my minimal reproducible code with working stackblitz.
id = signal(1);
rs = ResourceStatus;
http = inject(HttpClient);
resourceControl = signal(true);
resource: ResourceRef<any> = resource<ResourceRequest | undefined, any>({
request: (): ResourceRequest | undefined => {
return {
id: this.id(),
};
},
loader: ({ request: { id }, abortSignal }) => {
return fetch(`https://jsonplaceholder.typicode.com/todos/${id}`).then(
(res: any) => res.json()
);
},
});
<div>
<div>
Resource Request triggers:
</div>
<div>
<input [(ngModel)]="id" type="number"/>
</div>
</div>
<div>
@if(![rs.Loading, rs.Reloading].includes(resource.status())) {
{{resource.value() | json}}
} @else{
Loading...
}
</div>
From the documentation:
resource
will cancel in-progress loads via theAbortSignal
when destroyed or when a new request object becomes available, which could prematurely abort mutations.
The syntax to configure the AbortController
is as follows:
resource: ResourceRef<any> = resource<ResourceRequest | undefined, any>({
...
loader: ({ request: { id }, abortSignal }) => {
return fetch(`https://jsonplaceholder.typicode.com/todos/${id}`, {
signal: abortSignal
}).then((res: any) => res.json();
},
});
The signal
property from RequestInit Object - MDN Docs
An
AbortSignal
. If this option is set, the request can be canceled by callingabort()
on the corresponding AbortController.
So providing the AbortController
will do two things:
When a new request object arrives the previous still running requests are aborted (cancelled) - thus saving network resources.
When the resource is itself destroyed the still running requests are aborted.
In the below screenshots you can see the difference between using it and not using it:
For rxResource
this is not a prerequisite, since it automatically performs this action, similar to the switchMap
behavior of rxjs (does not use switchMap though).
import {
Component,
inject,
ResourceRef,
ResourceStatus,
signal,
resource,
} from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { rxResource } from '@angular/core/rxjs-interop';
import { HttpClient, provideHttpClient } from '@angular/common/http';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
export interface ResourceRequest {
id: number;
}
@Component({
selector: 'app-root',
imports: [CommonModule, FormsModule],
template: `
<div>
<div>
Resource Request triggers:
</div>
<div>
<input [(ngModel)]="id" type="number"/>
</div>
</div>
<div>
@if(![rs.Loading, rs.Reloading].includes(resource.status())) {
{{resource.value() | json}}
} @else{
Loading...
}
</div>
`,
})
export class App {
id = signal(1);
rs = ResourceStatus;
http = inject(HttpClient);
resourceControl = signal(true);
resource: ResourceRef<any> = resource<ResourceRequest | undefined, any>({
request: (): ResourceRequest | undefined => {
return {
id: this.id(),
};
},
loader: ({ request: { id }, abortSignal }) => {
return fetch(`https://jsonplaceholder.typicode.com/todos/${id}`, {
signal: abortSignal,
}).then((res: any) => res.json());
},
});
}
bootstrapApplication(App, {
providers: [provideHttpClient()],
});