Injects
injectPages
Bidirectional paginated data fetching with infinite scroll support using Signals
For usage patterns and examples, see the Infinite Queries guide.
Basic Usage
Given an API that returns:
{
"items": [
{ "id": 1, "title": "Post 1" },
{ "id": 2, "title": "Post 2" }
],
"meta": { "page": 1, "hasMore": true }
}@Component({
selector: "app-post-list",
template: `
@if (posts.loading()) {
<div>Loading...</div>
} @else {
@for (post of posts.data(); track post.id) {
<app-post-card [post]="post" />
}
@if (posts.canFetchNext()) {
<button (click)="posts.fetchNext()" [disabled]="posts.fetchingNext()">
{{ posts.fetchingNext() ? "Loading..." : "Load More" }}
</button>
}
}
`,
})
export class PostListComponent {
posts = injectPages(
(api) => api("posts").GET({ query: { page: 1, limit: 20 } }),
{
canFetchNext: ({ lastPage }) => lastPage?.data?.meta.hasMore ?? false,
nextPageRequest: ({ lastPage }) => ({
query: { page: (lastPage?.data?.meta.page ?? 0) + 1 },
}),
merger: (pages) => pages.flatMap((p) => p.data?.items ?? []),
}
);
}Options
| Option | Type | Required | Description |
|---|---|---|---|
merger | (pages) => TItem[] | Yes | Merge all pages into items |
canFetchNext | (ctx) => boolean | No | Check if next page exists. Default: () => false |
nextPageRequest | (ctx) => Partial<TRequest> | No | Build request for next page |
canFetchPrev | (ctx) => boolean | No | Check if previous page exists |
prevPageRequest | (ctx) => Partial<TRequest> | No | Build request for previous page |
enabled | boolean | Signal<bool> | No | Whether to fetch automatically |
Context Object
// For canFetchNext and nextPageRequest
type NextContext<TData, TRequest> = {
lastPage: InfinitePage<TData> | undefined;
pages: InfinitePage<TData>[];
request: TRequest;
};
// For canFetchPrev and prevPageRequest
type PrevContext<TData, TRequest> = {
firstPage: InfinitePage<TData> | undefined;
pages: InfinitePage<TData>[];
request: TRequest;
};
// Each page in the pages array
type InfinitePage<TData> = {
status: "pending" | "loading" | "success" | "error" | "stale";
data?: TData;
error?: TError;
meta?: TMeta;
input?: { query?; params?; body? };
};Returns
| Property | Type | Description |
|---|---|---|
data | Signal<TItem[] | undefined> | Merged items from all pages |
pages | Signal<InfinitePage<TData>[]> | Array of all pages with status, data, and meta |
loading | Signal<boolean> | True during initial load |
fetching | Signal<boolean> | True during any fetch |
fetchingNext | Signal<boolean> | True while fetching next page |
fetchingPrev | Signal<boolean> | True while fetching previous |
canFetchNext | Signal<boolean> | Whether next page exists |
canFetchPrev | Signal<boolean> | Whether previous page exists |
fetchNext | () => Promise<void> | Fetch the next page |
fetchPrev | () => Promise<void> | Fetch the previous page |
trigger | (options?) => Promise<void> | Trigger fetch with optional new request options |
abort | () => void | Abort current request |
error | Signal<TError | undefined> | Error if request failed |