@angular-experts/resource
Version:
### The missing create, update, delete (CUD) support for Angular resource
180 lines (175 loc) • 6.94 kB
TypeScript
import * as _angular_core from '@angular/core';
import { Observable, mergeMap } from 'rxjs';
type RequestType = 'create' | 'update' | 'remove';
type Behavior = 'concat' | 'merge' | 'switch' | 'exhaust';
/**
* Strategy for handling resource requests
*
* - `'pessimistic'` - wait for server response, then reload the entire collection
* - `'optimistic'` - update UI immediately and assume success
* - `'incremental'` - patch returned item into the existing collection after server confirms
*/
type Strategy = 'optimistic' | 'pessimistic' | 'incremental';
interface RestResourceOptions<T, ID> {
/**
* Whether to log verbose information to the console.
* This is useful for debugging and understanding the resource's behavior.
*
* @default false
*/
verbose?: boolean;
/**
* A reactive function which determines the request to be made.
* Whenever the params change, the loader will be triggered to fetch a new value for the resource.
*
* (works the same way as Angular's `resource`)
*
* @returns A string containing URL query parameters or undefined
*/
params?: () => string | undefined;
/**
* Function to extract the ID from an item when the ID is not stored in the `id` field.
* Used to identify items for update and remove operations.
*
* @param item The item from which to extract the ID
* @returns The ID of the item
*/
idSelector?: (item: T) => ID;
/**
* The default strategy for the resource for every request type,
* it can be overridden by the request-type specific strategy
*
* - `'pessimistic'` - wait for server response, then reload the entire collection
* - `'optimistic'` - update UI immediately and assume success
* - `'incremental'` - patch returned item into the existing collection after server confirms
*
* @default 'pessimistic'
*/
strategy?: Strategy;
create?: {
/**
* Defines how create requests are handled when multiple requests are made.
* Controls the RxJS flattening operator used for the HTTP request.
*
* @default 'concat'
*/
behavior?: Behavior;
/**
* Determines whether changes are applied optimistically (before server confirmation)
* or pessimistically (after server confirmation) for create operations.
*
* @default The value of the global strategy option
*/
strategy?: Strategy;
/**
* Optionally generate a new item ID (if the target API does not handle this)
* and set it on the item.
*
* @property generator A function that returns a new ID (e.g., UUID or auto-increment).
* Used when the backend does not assign one automatically.
*
* @property setter (Optional) A function that sets the generated ID onto the item,
* especially useful when the item’s ID field is not named `id`.
*
* @example
* {
* id: {
* generator: () => uuid(),
* setter: (id, item) => {
* item.nonstandardId = id
* }
* }
* }
*
*/
id?: {
/**
* A function that generates a new unique ID.
* @example
* () => uuid()
*/
generator: () => ID;
/**
* (Optional) Custom logic for assigning the generated ID to the item.
*
* @param id The generated ID
* @param item The item object to modify
*
* @example
* setter: (id, item) => {
* item.nonstandardId = id
* }
*/
setter?: (id: ID, item: Partial<T>) => void;
};
};
update?: {
/**
* Defines how update requests are handled when multiple requests are made.
* Controls the RxJS flattening operator used for the HTTP request.
*
* @default 'concat'
*/
behavior?: Behavior;
/**
* Determines whether changes are applied optimistically (before server confirmation)
* or pessimistically (after server confirmation) for update operations.
*
* @default The value of the global strategy option
*/
strategy?: Strategy;
};
remove?: {
/**
* Defines how remove requests are handled when multiple requests are made.
* Controls the RxJS flattening operator used for the HTTP request.
*
* @default 'concat'
*/
behavior?: Behavior;
/**
* Determines whether changes are applied optimistically (before server confirmation)
* or pessimistically (after server confirmation) for remove operations.
*
* @default The value of the global strategy option
*/
strategy?: Strategy;
};
}
declare const LOG_PREFIX = "[@angular-experts/resource]";
declare function restResource<T, ID, E extends Error = Error>(apiEndpoint: string, options?: RestResourceOptions<T, ID>): {
loadingInitial: _angular_core.Signal<boolean>;
loading: _angular_core.Signal<boolean>;
loadingCreate: _angular_core.WritableSignal<boolean>;
loadingUpdate: _angular_core.WritableSignal<boolean>;
loadingRemove: _angular_core.WritableSignal<boolean>;
errorRead: _angular_core.Signal<Error | undefined>;
errorCreate: _angular_core.WritableSignal<E | undefined>;
errorUpdate: _angular_core.WritableSignal<E | undefined>;
errorRemove: _angular_core.WritableSignal<E | undefined>;
/**
* The value of the resource, ONLY if the resource is a single item.
*
* This is useful when building a resource to manage a single entity, eg detail view.
*
* @returns Signal of single item or undefined (also returns undefined if resource has more than one item)
*/
value: _angular_core.Signal<T | undefined>;
/**
* The values of the resource (eg list of 0 to n items).
*
* @returns Signal of a list of items or undefined (also returns undefined if resource has no items)
*/
values: _angular_core.WritableSignal<T[] | undefined>;
hasValue: _angular_core.Signal<boolean>;
hasValues: _angular_core.Signal<boolean>;
reload: () => boolean;
create: (item: Partial<T>) => void;
update: (item: T) => void;
remove: (item: T) => void;
destroy: () => void;
};
declare function streamify<T extends unknown[]>(impl: (stream: Observable<T>) => Observable<unknown>): (...args: T) => void;
declare function behaviorToOperator(behavior?: Behavior): typeof mergeMap;
export { LOG_PREFIX, behaviorToOperator, restResource, streamify };
export type { Behavior, RequestType, RestResourceOptions, Strategy };