ngx-primeng-toolkit
Version:
A comprehensive TypeScript utility library for Angular component state management, PrimeNG table state management, ng-select helpers, data storage, and memoized HTTP caching. Compatible with Angular 19+ and PrimeNG 19+ (optimized for Angular 20+ and Prime
1,498 lines (1,487 loc) • 53 kB
TypeScript
import { HttpClient, HttpContextToken } from '@angular/common/http';
import * as _angular_core from '@angular/core';
import { Signal, DestroyRef } from '@angular/core';
import { TableLazyLoadEvent, Table } from 'primeng/table';
import { z } from 'zod';
import { SelectItem } from 'primeng/api';
import { Subject, Observable } from 'rxjs';
/**
* Makes all properties of T nullable (T | null) recursively, handling functions, arrays, and objects
* @template Thing - The type to make nullable
* @example
* ```typescript
* type User = { id: number; name: string; email: string; tags: string[] };
* type NullableUser = RecursiveNullable<User>;
* // Result: {
* // id: number | null;
* // name: string | null;
* // email: string | null;
* // tags: (string | null)[] | null;
* // }
* ```
*/
type RecursiveNullable<Thing> = Thing extends Function ? Thing : Thing extends Array<infer InferredArrayMember> ? RecursiveNullableArray<InferredArrayMember> : Thing extends Record<string, any> ? RecursiveNullableObject<Thing> : Exclude<Thing, undefined> | null;
type RecursiveNullableObject<Thing extends object> = {
[Key in keyof Thing]: RecursiveNullable<Thing[Key]>;
};
interface RecursiveNullableArray<Thing> extends Array<RecursiveNullable<Thing>> {
}
/**
* Makes all properties of T nullish (T | null | undefined)
* @template T - The base type
* @example
* ```typescript
* type User = { id: number; name: string };
* type NullishUser = Nullish<User>;
* // Result: { id: number | null | undefined; name: string | null | undefined }
* ```
*/
type Nullish<T> = {
[P in keyof T]: Exclude<T[P], null | undefined> | null | undefined;
};
/**
* Makes all properties of T nullish (T | null | undefined) recursively, handling functions, arrays, and objects
* @template Thing - The type to make nullish
* @example
* ```typescript
* type User = { id: number; profile: { name: string; age: number }; tags: string[] };
* type NullishUser = RecursiveNullish<User>;
* // Result: {
* // id: number | null | undefined;
* // profile: {
* // name: string | null | undefined;
* // age: number | null | undefined
* // } | null | undefined;
* // tags: (string | null | undefined)[] | null | undefined;
* // }
* ```
*/
type RecursiveNullish<Thing> = Thing extends Function ? Thing : Thing extends Array<infer InferredArrayMember> ? RecursiveNullishArray<InferredArrayMember> : Thing extends Record<string, any> ? RecursiveNullishObject<Thing> : Exclude<Thing, null | undefined> | null | undefined;
type RecursiveNullishObject<Thing extends object> = {
[Key in keyof Thing]: RecursiveNullish<Thing[Key]>;
};
interface RecursiveNullishArray<Thing> extends Array<RecursiveNullish<Thing>> {
}
/**
* Makes all properties optional recursively, useful for partial updates, handling functions, arrays, and objects
* @template Thing - The type to make recursively partial
* @example
* ```typescript
* type User = {
* id: number;
* profile: { name: string; age: number };
* settings: { theme: string; notifications: boolean };
* tags: string[];
* };
* type PartialUser = RecursivePartial<User>;
* // Result: {
* // id?: number | undefined;
* // profile?: { name?: string | undefined; age?: number | undefined } | undefined;
* // settings?: { theme?: string | undefined; notifications?: boolean | undefined } | undefined;
* // tags?: (string | undefined)[] | undefined;
* // }
* ```
*/
type RecursivePartial<Thing> = Thing extends Function ? Thing : Thing extends Array<infer InferredArrayMember> ? RecursivePartialArray<InferredArrayMember> : Thing extends object ? RecursivePartialObject<Thing> : Thing | undefined;
type RecursivePartialObject<Thing> = {
[Key in keyof Thing]?: RecursivePartial<Thing[Key]>;
};
interface RecursivePartialArray<Thing> extends Array<RecursivePartial<Thing>> {
}
/**
* Enumeration for manipulation types in component operations
* Used for tracking the current operation state in component management
*
* @example
* ```typescript
* // In a component
* currentOperation: ManipulationType = ManipulationType.Create;
*
* // Check operation type
* if (this.currentOperation === ManipulationType.Update) {
* // Handle update logic
* }
* ```
*/
declare enum ManipulationType {
/** Creating a new item */
Create = "Create",
/** Updating an existing item */
Update = "Update",
/** Creating a child item */
CreateChild = "Create Child",
/** Deleting an item */
Delete = "Delete",
/** Viewing item details */
View = "View",
/** Saving an item */
Save = "Save"
}
/**
* Key-value pair type for common data structures
* @template K The type of the key
* @template D The type of the data
*/
interface KeyData<K, D> {
key: K;
data: D;
}
/**
* Common API response wrapper type
* @template T The type of the data payload
*/
interface ApiResponse<T> {
data: T;
message?: string;
status: number;
success: boolean;
}
/**
* Pagination metadata interface
*/
interface PaginationMeta {
currentPage: number;
totalPages: number;
totalItems: number;
itemsPerPage: number;
hasNextPage: boolean;
hasPreviousPage: boolean;
}
/**
* Paginated response type
* @template T The type of the data items
*/
interface PaginatedResponse<T> {
data: T[];
meta: PaginationMeta;
}
/**
* Query parameters for pagination
*/
interface PaginationParams {
page?: number;
limit?: number;
offset?: number;
}
/**
* Common sort parameters
*/
interface SortParams {
sortBy?: string;
sortOrder?: "asc" | "desc";
}
/**
* Combined query parameters for API requests
*/
type QueryParams = Record<string, string | number | boolean> & Partial<PaginationParams> & Partial<SortParams>;
/**
* String filter types for PrimeNG table filtering
*/
type StringFilterType = "startsWith" | "notStartsWith" | "endsWith" | "notEndsWith" | "contains" | "notContains";
/**
* Numeric filter types for PrimeNG table filtering
*/
type NumericFilterType = "equals" | "notEquals" | "greaterThan" | "lessThan" | "greaterThanOrEqual" | "lessThanOrEqual";
/**
* Boolean filter types for PrimeNG table filtering
*/
type BooleanFilterType = Extract<NumericFilterType, "equals" | "notEquals">;
/**
* Combined filter types
*/
type FilterType = StringFilterType | NumericFilterType;
/**
* Filter type mappings for backend API
*/
type FilterTypeMapped = "starts" | "!starts" | "ends" | "!ends" | "like" | "!like" | "=" | "!=" | ">" | "<" | ">=" | "<=";
/**
* PrimeNG table header configuration interface
*/
interface PrimeNgTableHeader {
identifier: {
label?: string;
field: string;
hasSort?: boolean;
isBoolean?: boolean;
isNested?: boolean;
isDate?: boolean;
isDateTime?: boolean;
styleClass?: string;
};
filter?: {
type: "text" | "numeric" | "boolean" | "date" | "dropdown" | "multiselect";
placeholder?: string;
matchModeOptions?: any[];
defaultMatchMode: FilterType;
ariaLabel?: string;
colspan?: number;
styleClass?: Record<string, string>;
};
}
/**
* Dynamic query DTO interface
*/
interface DynamicQueryDto {
size: number;
page: number;
filter: DynamicQueryFilterDto[];
sort: DynamicQuerySortDto[];
}
/**
* Filter DTO for dynamic queries
*/
interface DynamicQueryFilterDto {
field: string;
value: string;
type: FilterTypeMapped;
}
/**
* Sort DTO for dynamic queries
*/
interface DynamicQuerySortDto {
field: string;
dir: "asc" | "desc";
}
/**
* Paged data response interface
*/
interface DynamicQueryPagedDataResponse<T> {
data: T[];
last_page: number;
last_row: number;
}
/**
* Paged data response interface for simple pagination
*/
interface PagedDataResponse<T> {
payload: T[];
totalCount: number;
}
/**
* Internal table state interface for dynamic table
*/
interface PrimeNgTableState<T> {
data: Array<T>;
isLoading: boolean;
size: number;
page: number;
totalRecords: number;
filter: DynamicQueryFilterDto[];
sort: DynamicQuerySortDto[];
}
/**
* Internal state interface for paged table
*/
interface PrimeNgPagedTableState<T> {
data: Array<T>;
isLoading: boolean;
totalRecords: number;
limit: number;
page: number;
}
/**
* Query DTO interface for paged data requests
*/
interface PagedDataQueryDto {
limit: number;
page: number;
}
/**
* Query parameters type for additional HTTP request parameters
*/
type PrimeNgTableStateHelperQueryParam = Record<string, string | number | boolean>;
/**
* Zod schema for dynamic query response validation
*/
declare const dynamicQueryResponseZodSchema: z.ZodObject<{
data: z.ZodArray<z.ZodAny, "many">;
last_page: z.ZodNumber;
last_row: z.ZodNumber;
}, "strip", z.ZodTypeAny, {
data: any[];
last_page: number;
last_row: number;
}, {
data: any[];
last_page: number;
last_row: number;
}>;
/**
* Zod schema for paged data response validation
*/
declare const PagedDataResponseZodSchema: z.ZodObject<{
payload: z.ZodArray<z.ZodAny, "many">;
totalCount: z.ZodNumber;
}, "strip", z.ZodTypeAny, {
payload: any[];
totalCount: number;
}, {
payload: any[];
totalCount: number;
}>;
/**
* Error response interface
*/
interface ErrorResponse {
error: string;
message: string;
statusCode: number;
timestamp: string;
}
/**
* Creates a key-value pair object
* @param key The key value
* @param data The data value
* @returns KeyData object
*/
declare function createKeyData<K, D>(key: K, data: D): KeyData<K, D>;
/**
* Type guard to check if a response is an API response
* @param response The response to check
* @returns true if response is ApiResponse, false otherwise
*/
declare function isApiResponse<T>(response: any): response is ApiResponse<T>;
/**
* Type guard to check if a response is paginated
* @param response The response to check
* @returns true if response is PaginatedResponse, false otherwise
*/
declare function isPaginatedResponse<T>(response: any): response is PaginatedResponse<T>;
/**
* Type guard to check if a response is a simple paged response
* @param response The response to check
* @returns true if response is PagedDataResponse, false otherwise
*/
declare function isSimplePagedResponse<T>(response: any): response is PagedDataResponse<T>;
/**
* Type guard to check if a response is a dynamic query response
* @param response The response to check
* @returns true if response is PagedDataResponse, false otherwise
*/
declare function isDynamicQueryResponse<T>(response: any): response is DynamicQueryPagedDataResponse<T>;
type NestableColumn = {
isNested?: boolean;
};
/**
* Options for creating PrimeNgDynamicTableStateHelper
*/
type PrimeNgDynamicTableStateOpts = {
url: string;
httpClient: HttpClient;
skipLoadingSpinner?: boolean;
};
/**
* PrimeNG Dynamic Table State Helper class for managing table state with lazy loading, filtering, and sorting
*
* This helper provides advanced table functionality including:
* - Lazy loading with pagination
* - Column filtering with multiple filter types
* - Multi-column sorting
* - State management with NgRx Signals
* - Automatic API integration
* - Route parameter support
* - Query parameter management
*
* @example
* ```typescript
* const tableState = PrimeNgDynamicTableStateHelper.create<User>({
* url: '/api/users',
* httpClient: this.httpClient
* });
*
* // In template
* <p-table [value]="tableState.data()"
* [lazy]="true"
* [loading]="tableState.isLoading()"
* [totalRecords]="tableState.totalRecords()"
* (onLazyLoad)="tableState.onLazyLoad($event)">
* </p-table>
* ```
*/
declare class PrimeNgDynamicTableStateHelper<T> {
#private;
private url;
private readonly httpClient;
private readonly state;
private urlWithOutRouteParam;
private skipLoadingSpinner;
readonly uniqueKey: Signal<string>;
readonly totalRecords: Signal<number>;
readonly isLoading: Signal<boolean>;
readonly data: Signal<Array<T>>;
private constructor();
/**
* Creates a new instance of PrimeNgDynamicTableStateHelper
* @param options - Configuration options
* @returns New instance of PrimeNgDynamicTableStateHelper
*/
static create<T>(options: PrimeNgDynamicTableStateOpts): PrimeNgDynamicTableStateHelper<T>;
/**
* Sets whether to skip the loading spinner
* @param skip - Whether to skip the loading spinner
* @returns This instance for method chaining
*/
setSkipLoadingSpinner(skip: boolean): this;
/**
* Sets the unique key field for table rows
* @param newUniqueKey - The field name to use as unique identifier
* @returns This instance for method chaining
*/
setUniqueKey(newUniqueKey: string): this;
/**
* Updates the API URL
* @param newUrl - The new API URL
* @returns This instance for method chaining
*/
setUrl(newUrl: string): this;
/**
* Appends a route parameter to the URL
* @param newRouteParam - The route parameter to append
* @returns This instance for method chaining
*/
setRouteParam(newRouteParam: string): this;
/**
* Patches existing query parameters
* @param value - Query parameters to merge
* @returns This instance for method chaining
*/
patchQueryParams(value: PrimeNgTableStateHelperQueryParam): this;
/**
* Removes a specific query parameter
* @param key - The key to remove
* @returns This instance for method chaining
*/
removeQueryParam(key: string): this;
/**
* Sets all query parameters (replaces existing)
* @param newQueryParams - New query parameters
* @returns This instance for method chaining
*/
setQueryParams(newQueryParams: PrimeNgTableStateHelperQueryParam): this;
/**
* Handles PrimeNG table lazy load events
* @param event - The lazy load event from PrimeNG table
*/
onLazyLoad(event: TableLazyLoadEvent): Promise<void>;
/**
* Clears table data and resets to first page
* @param table - Optional PrimeNG Table reference to reset
*/
clearTableData(table?: Table): Promise<void>;
/**
* Manually triggers data refresh with current state
*/
refresh(): Promise<void>;
/**
* Fetches data from the API
*/
private fetchData;
/**
* Builds the DTO for API requests
*/
private dtoBuilder;
/**
* Maps PrimeNG filters to API filter format
*/
private filterMapper;
/**
* Maps PrimeNG filter match modes to API filter types
*/
private evaluateInput;
}
/**
* Options for creating PrimengPagedDataTableStateHelper
*/
type PrimeNgPagedTableStateOpts = {
url: string;
httpClient: HttpClient;
skipLoadingSpinner?: boolean;
};
/**
* Simple paged data table state helper for basic pagination without filtering
*
* This helper provides basic table functionality including:
* - Simple pagination (page and limit only)
* - Basic state management with NgRx Signals
* - API integration for paged data
* - Route parameter support
* - Query parameter management
*
* Use this when you need simple pagination without complex filtering and sorting.
* For advanced features, use PrimeNgDynamicTableStateHelper instead.
*
* @example
* ```typescript
* const tableState = PrimengPagedDataTableStateHelper.create<Product>({
* url: '/api/products',
* httpClient: this.httpClient
* });
*
* // In template
* <p-table [value]="tableState.data()"
* [lazy]="true"
* [loading]="tableState.isLoading()"
* [totalRecords]="tableState.totalRecords()"
* (onLazyLoad)="tableState.onLazyLoad($event)">
* </p-table>
* ```
*/
declare class PrimeNgPagedDataTableStateHelper<T> {
#private;
private url;
private readonly httpClient;
private urlWithOutRouteParam;
private skipLoadingSpinner;
readonly uniqueKey: Signal<string>;
readonly totalRecords: Signal<number>;
readonly isLoading: Signal<boolean>;
readonly data: Signal<Array<T>>;
readonly currentPage: Signal<number>;
readonly currentPageSize: Signal<number>;
private constructor();
/**
* Creates a new instance of PrimengPagedDataTableStateHelper
* @param option - Configuration options
* @returns New instance of PrimengPagedDataTableStateHelper
*/
static create<T>(option: PrimeNgPagedTableStateOpts): PrimeNgPagedDataTableStateHelper<T>;
/**
* Creates a new instance without initial URL (can be set later)
* @param option - Configuration options without URL
* @returns New instance of PrimengPagedDataTableStateHelper
*/
static createWithBlankUrl<T>(option: Omit<PrimeNgPagedTableStateOpts, "url">): PrimeNgPagedDataTableStateHelper<T>;
/**
* Sets whether to skip the loading spinner
* @param skip - Whether to skip the loading spinner
* @returns This instance for method chaining
*/
setSkipLoadingSpinner(skip: boolean): this;
/**
* Sets the unique key field for table rows
* @param newUniqueKey - The field name to use as unique identifier
* @returns This instance for method chaining
*/
setUniqueKey(newUniqueKey: string): this;
/**
* Updates the API URL
* @param newUrl - The new API URL
* @returns This instance for method chaining
*/
setUrl(newUrl: string): this;
/**
* Appends a route parameter to the URL
* @param newRouteParam - The route parameter to append
* @returns This instance for method chaining
*/
setRouteParam(newRouteParam: string): this;
/**
* Patches existing query parameters
* @param value - Query parameters to merge
* @returns This instance for method chaining
*/
patchQueryParams(value: PrimeNgTableStateHelperQueryParam): this;
/**
* Removes a specific query parameter
* @param key - The key to remove
* @returns This instance for method chaining
*/
removeQueryParam(key: string): this;
/**
* Removes all query parameters
* @returns This instance for method chaining
*/
removeAllQueryParams(): this;
/**
* Sets all query parameters (replaces existing)
* @param newQueryParams - New query parameters
* @returns This instance for method chaining
*/
setQueryParams(newQueryParams: PrimeNgTableStateHelperQueryParam): this;
/**
* Handles PrimeNG table lazy load events
* @param event - The lazy load event from PrimeNG table
*/
onLazyLoad(event: TableLazyLoadEvent): Promise<void>;
/**
* Clears table data and resets to first page
* @param table - Optional PrimeNG Table reference to reset
*/
clearTableData(table?: Table): Promise<void>;
/**
* Manually triggers data refresh with current state
*/
refresh(): Promise<void>;
/**
* Fetches data from the API
*/
private fetchData;
/**
* Builds the DTO for API requests
*/
private dtoBuilder;
}
/**
* HTTP Context Token to skip loading spinner on HTTP requests
* Usage: Set this token to true in HttpContext to skip showing loading spinner
*
* @example
* ```typescript
* const context = new HttpContext().set(SkipLoadingSpinner, true);
* this.httpClient.get('/api/data', { context });
* ```
*/
declare const SkipLoadingSpinner: HttpContextToken<boolean>;
/**
* Creates PrimeNG SelectItem array for numeric filter match modes
* @param styleClass - CSS class for styling the options
* @param disabled - Whether the options should be disabled
* @returns Array of SelectItem for numeric filters
*/
declare function createPrimengNumberMatchModes(styleClass?: string, disabled?: boolean): SelectItem<NumericFilterType>[];
/**
* Creates PrimeNG SelectItem array for string filter match modes
* @param styleClass - CSS class for styling the options
* @param disabled - Whether the options should be disabled
* @returns Array of SelectItem for string filters
*/
declare function createPrimengStringMatchModes(styleClass?: string, disabled?: boolean): SelectItem<StringFilterType>[];
/**
* Creates a complete table header configuration for text columns
* @param field - The field name for the column
* @param label - Display label for the column header
* @param options - Additional configuration options
* @returns Complete PrimeNgTableHeader configuration
*/
declare function createTextColumn(field: string, label: string, options?: {
hasSort?: boolean;
hasFilter?: boolean;
placeholder?: string;
matchModeOptions?: SelectItem<StringFilterType>[];
defaultMatchMode?: StringFilterType;
styleClass?: string;
filterStyleClass?: Record<string, string>;
} & NestableColumn): PrimeNgTableHeader;
/**
* Creates a complete table header configuration for numeric columns
* @param field - The field name for the column
* @param label - Display label for the column header
* @param options - Additional configuration options
* @returns Complete PrimeNgTableHeader configuration
*/
declare function createNumericColumn(field: string, label: string, options?: {
hasSort?: boolean;
hasFilter?: boolean;
placeholder?: string;
matchModeOptions?: SelectItem<NumericFilterType>[];
defaultMatchMode?: NumericFilterType;
styleClass?: string;
filterStyleClass?: Record<string, string>;
} & NestableColumn): PrimeNgTableHeader;
/**
* Creates a complete table header configuration for boolean columns
* @param field - The field name for the column
* @param label - Display label for the column header
* @param options - Additional configuration options
* @returns Complete PrimeNgTableHeader configuration
*/
declare function createBooleanColumn(field: string, label: string, options?: {
hasSort?: boolean;
hasFilter?: boolean;
styleClass?: string;
filterStyleClass?: Record<string, string>;
} & NestableColumn): PrimeNgTableHeader;
/**
* Creates a complete table header configuration for date columns
* @param field - The field name for the column
* @param label - Display label for the column header
* @param options - Additional configuration options
* @returns Complete PrimeNgTableHeader configuration
*/
declare function createDateColumn(field: string, label: string, options?: {
hasSort?: boolean;
hasFilter?: boolean;
placeholder?: string;
styleClass?: string;
filterStyleClass?: Record<string, string>;
} & NestableColumn): PrimeNgTableHeader;
/**
* Creates a complete table header configuration for dropdown columns
* @param field - The field name for the column
* @param label - Display label for the column header
* @param dropdownOptions - Options for the dropdown filter
* @param options - Additional configuration options
* @returns Complete PrimeNgTableHeader configuration
*/
declare function createDropdownColumn(field: string, label: string, dropdownOptions: SelectItem[], options?: {
hasSort?: boolean;
hasFilter?: boolean;
placeholder?: string;
styleClass?: string;
filterStyleClass?: Record<string, string>;
}): PrimeNgTableHeader;
/**
* Creates a complete table header configuration for multiselect columns
* @param field - The field name for the column
* @param label - Display label for the column header
* @param multiselectOptions - Options for the multiselect filter
* @param options - Additional configuration options
* @returns Complete PrimeNgTableHeader configuration
*/
declare function createMultiselectColumn(field: string, label: string, multiselectOptions: SelectItem[], options?: {
hasSort?: boolean;
hasFilter?: boolean;
placeholder?: string;
styleClass?: string;
filterStyleClass?: Record<string, string>;
}): PrimeNgTableHeader;
/**
* Creates a simple table header configuration without filtering
* @param field - The field name for the column
* @param label - Display label for the column header
* @param options - Additional configuration options
* @returns Simple PrimeNgTableHeader configuration
*/
declare function createSimpleColumn(field: string, label: string, options?: {
hasSort?: boolean;
styleClass?: string;
} & NestableColumn): PrimeNgTableHeader;
/**
* Utility function to merge multiple table header configurations
* @param headers - Array of table header configurations
* @returns Array of merged headers
*/
declare function mergeTableHeaders(...headers: PrimeNgTableHeader[]): PrimeNgTableHeader[];
/**
* Utility function to create boolean SelectItem options
* @param trueLabel - Label for true value
* @param falseLabel - Label for false value
* @returns Array of SelectItem for boolean values
*/
declare function createBooleanSelectItems(trueLabel?: string, falseLabel?: string): SelectItem[];
/**
* Utility function to create status SelectItem options
* @param statusOptions - Object mapping status values to labels
* @returns Array of SelectItem for status values
*/
declare function createStatusSelectItems(statusOptions: Record<string | number, string>): SelectItem[];
/**
* Utility function to remove null and undefined values from an object
*
* This is particularly useful when setting query parameters, as null/undefined
* values should typically be filtered out before sending to APIs.
*
* @param o The object to clean
* @returns A new object with null and undefined values removed
*
* @example
* ```typescript
* const queryParams = {
* name: 'John',
* age: null,
* email: 'john@example.com',
* phone: undefined
* };
*
* const cleaned = cleanNullishFromObject(queryParams);
* // Result: { name: 'John', email: 'john@example.com' }
*
* // Use case with table state
* this.tableState.setQueryParams(cleanNullishFromObject(queryParams));
* ```
*/
declare function cleanNullishFromObject(o: object): Record<string, any>;
declare function hasNullishInObject(obj: object): boolean;
declare function routeParamConcat(baseUrl: string, routeParam: number | string): string;
declare function binarySearch<TItem>(arr: TItem[], val: TItem): number;
declare function emptyCallback(): void;
declare function nullableKeyData<TKey, TData>(key: TKey | null, data: TData | null): KeyData<TKey, TData> | null;
declare class ReloadNotification {
static create(): ReloadNotification;
}
/**
* A generic class for memoizing data storage with HTTP caching capabilities
* Provides methods to load and cache single data objects or arrays with automatic loading states
*
* @template T The type of data to be stored and managed
*
* @example
* ```typescript
* interface User { id: number; name: string; }
*
* const userStorage = new MemoizedDataStorage<User>(httpClient);
* await userStorage.loadSingleData('/api/user/1');
* console.log(userStorage.singleData()); // User data or null
*
* const usersStorage = new MemoizedDataStorage<User>(httpClient);
* await usersStorage.loadMultipleData('/api/users');
* console.log(usersStorage.multipleData()); // Array of User data
* ```
*/
declare class MemoizedDataStorage<T> {
#private;
readonly httpClient: HttpClient;
private skipLoadingSpinner;
/**
* Creates a new instance of MemoizedDataStorage
* @param httpClient Angular HttpClient instance for making HTTP requests
* @param skipLoadingSpinner Whether to skip the loading spinner for HTTP requests
*/
constructor(httpClient: HttpClient, skipLoadingSpinner?: boolean);
/**
* Sets whether to skip the loading spinner for HTTP requests
* @param skip Whether to skip the loading spinner
* @returns This instance for method chaining
*/
setSkipLoadingSpinner(skip: boolean): this;
/**
* Read-only signal containing single data object or null
*/
readonly singleData: _angular_core.Signal<T | null>;
/**
* Read-only signal containing array of data objects
*/
readonly multipleData: _angular_core.Signal<T[]>;
/**
* Read-only signal indicating whether a request is currently loading
*/
readonly isLoading: _angular_core.Signal<boolean>;
/**
* Disables memoization for the next read operation and clears cached data
* This forces the next loadSingleData or loadMultipleData call to fetch fresh data
*
* @example
* ```typescript
* const storage = new MemoizedDataStorage<User>(httpClient);
* await storage.loadSingleData('/api/user/1'); // Fetches data
* await storage.loadSingleData('/api/user/1'); // Returns cached data
*
* storage.disableMemoizationOnNextRead();
* await storage.loadSingleData('/api/user/1'); // Fetches fresh data
* ```
*/
disableMemoizationOnNextRead(): void;
/**
* Loads a single data object from the specified URL with optional query parameters
* Uses memoization to avoid redundant requests unless explicitly disabled
*
* @param url The URL to fetch data from
* @param queryParams Optional query parameters to include in the request
* @returns Promise that resolves when the data is loaded
* @throws Error if the HTTP request fails
*
* @example
* ```typescript
* const storage = new MemoizedDataStorage<User>(httpClient);
* await storage.loadSingleData('/api/user/1', { include: 'profile' });
* const user = storage.singleData(); // User data or null
* ```
*/
loadSingleData(url: string, queryParams?: Record<string, string | number>): Promise<void>;
/**
* Loads multiple data objects from the specified URL with optional query parameters
* Uses memoization to avoid redundant requests unless explicitly disabled
*
* @param url The URL to fetch data from
* @param queryParams Optional query parameters to include in the request
* @returns Promise that resolves when the data is loaded
* @throws Error if the HTTP request fails
*
* @example
* ```typescript
* const storage = new MemoizedDataStorage<User>(httpClient);
* await storage.loadMultipleData('/api/users', { page: 1, limit: 10 });
* const users = storage.multipleData(); // Array of User data
* ```
*/
loadMultipleData(url: string, queryParams?: Record<string, string | number>): Promise<void>;
/**
* Clears all cached data and resets the storage to initial state
*
* @example
* ```typescript
* const storage = new MemoizedDataStorage<User>(httpClient);
* await storage.loadSingleData('/api/user/1');
* storage.clear(); // Clears cached data
* console.log(storage.singleData()); // null
* console.log(storage.multipleData()); // []
* ```
*/
clear(): void;
/**
* Checks if single data is currently cached
* @returns true if single data is cached, false otherwise
*/
hasSingleData(): boolean;
/**
* Checks if multiple data is currently cached
* @returns true if multiple data is cached (non-empty array), false otherwise
*/
hasMultipleData(): boolean;
}
/**
* A reactive state management class for Angular components using signals
* Provides common component state properties and computed values
*
* @example
* ```typescript
* @Component({
* selector: 'app-user-management',
* template: `
* <h2>{{ componentState.componentTitleWithManipulationType() }}</h2>
* <p-button
* [loading]="componentState.isAnyAjaxOperationRunning()"
* [disabled]="componentState.isAnyAjaxOperationRunning()">
* Save
* </p-button>
* `
* })
* export class UserManagementComponent {
* componentState = new ComponentState()
* .updateComponentTitle('User')
* .updateCheckBoxSelectionStatus(true);
* }
* ```
*/
declare class ComponentState {
readonly isAjaxDataIncoming: _angular_core.WritableSignal<boolean>;
readonly enableCheckBoxSelection: _angular_core.WritableSignal<boolean>;
readonly isSelectableRowEnabled: _angular_core.WritableSignal<boolean>;
readonly isAjaxRequestOutgoing: _angular_core.WritableSignal<boolean>;
readonly hasMultipleSelection: _angular_core.WritableSignal<boolean>;
readonly isCreateOrUpdateDialogOpen: _angular_core.WritableSignal<boolean>;
readonly isUpdateDialogOpen: _angular_core.WritableSignal<boolean>;
readonly isCreateDialogOpen: _angular_core.WritableSignal<boolean>;
readonly manipulationType: _angular_core.WritableSignal<ManipulationType>;
readonly componentTitle: _angular_core.WritableSignal<string>;
/**
* Updates the component title
* @param componentTitle - The new title for the component
* @returns This instance for method chaining
*/
updateComponentTitle: (componentTitle: string) => this;
/**
* Updates the multiple selection status
* @param newStatus - Whether multiple selection is enabled
* @returns This instance for method chaining
*/
updateMultipleSelectionStatus: (newStatus: boolean) => this;
/**
* Updates the checkbox selection status
* @param newStatus - Whether checkbox selection is enabled
* @returns This instance for method chaining
*/
updateCheckBoxSelectionStatus: (newStatus: boolean) => this;
/**
* Updates the selectable row status
* @param newStatus - Whether row selection is enabled
* @returns This instance for method chaining
*/
updateSelectableRowStatus: (newStatus: boolean) => this;
/**
* Updates the manipulation type (Create, Update, Delete, View)
* @param type - The manipulation type
* @returns This instance for method chaining
*/
updateManipulationType: (type: ManipulationType) => this;
/**
* Sets the incoming Ajax data status
* @param status - Whether Ajax data is incoming
* @returns This instance for method chaining
*/
setAjaxDataIncoming: (status: boolean) => this;
/**
* Sets the outgoing Ajax request status
* @param status - Whether Ajax request is outgoing
* @returns This instance for method chaining
*/
setAjaxRequestOutgoing: (status: boolean) => this;
/**
* Sets the create or update dialog open status
* @param status - Whether the dialog is open
* @returns This instance for method chaining
*/
setCreateOrUpdateDialogOpen: (status: boolean) => this;
/**
* Sets the update dialog open status
* @param status - Whether the update dialog is open
* @returns This instance for method chaining
*/
setUpdateDialogOpen: (status: boolean) => this;
/**
* Sets the create dialog open status
* @param status - Whether the create dialog is open
* @returns This instance for method chaining
*/
setCreateDialogOpen: (status: boolean) => this;
/**
* Computed signal that combines component title with manipulation type
*/
readonly componentTitleWithManipulationType: _angular_core.Signal<string>;
/**
* Computed signal that indicates if component is in update state
*/
readonly isOnUpdateState: _angular_core.Signal<boolean>;
/**
* Computed signal that indicates if component is in create state
*/
readonly isOnCreateState: _angular_core.Signal<boolean>;
/**
* Computed signal that indicates if component is in delete state
*/
readonly isOnDeleteState: _angular_core.Signal<boolean>;
/**
* Computed signal that indicates if component is in view state
*/
readonly isOnViewState: _angular_core.Signal<boolean>;
/**
* Computed signal that indicates if any Ajax operation is currently running
*/
readonly isAnyAjaxOperationRunning: _angular_core.Signal<boolean>;
/**
* Computed signal that indicates if any dialog is open
*/
readonly isAnyDialogOpen: _angular_core.Signal<boolean>;
/**
* Resets all state to default values
* @returns This instance for method chaining
*/
reset: () => this;
}
/**
* A generic component data storage class using Angular signals
* Provides reactive data management for both single objects and arrays
*
* @template T The type of data to be stored and managed
*
* @example
* ```typescript
* interface User {
* id: number;
* name: string;
* email: string;
* }
*
* @Component({
* selector: 'app-user-list',
* template: `
* <!-- Single user display -->
* @if (dataStorage.singleData(); as user) {
* <div>
* <h3>{{ user.name }}</h3>
* <p>{{ user.email }}</p>
* </div>
* }
*
* <!-- Multiple users display -->
* @for (user of dataStorage.multipleData(); track user.id) {
* <div>{{ user.name }} - {{ user.email }}</div>
* }
* `
* })
* export class UserListComponent {
* dataStorage = new ComponentDataStorage<User>();
*
* ngOnInit() {
* // Set initial data
* this.dataStorage
* .updateSingleData({ id: 1, name: 'John', email: 'john@example.com' })
* .updateMultipleData([
* { id: 1, name: 'John', email: 'john@example.com' },
* { id: 2, name: 'Jane', email: 'jane@example.com' }
* ]);
* }
* }
* ```
*/
declare class ComponentDataStorage<T> {
readonly singleData: _angular_core.WritableSignal<T | null>;
readonly multipleData: _angular_core.WritableSignal<T[]>;
/**
* Patches multiple data by appending new data to the existing array
* @param newData - Array of new data to append
* @returns This instance for method chaining
*
* @example
* ```typescript
* const storage = new ComponentDataStorage<User>();
* storage.updateMultipleData([{ id: 1, name: 'John' }]);
* storage.patchMultipleData([{ id: 2, name: 'Jane' }]);
* // Result: [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }]
* ```
*/
patchMultipleData(newData: Array<T>): this;
/**
* Patches single data by merging new properties with existing data
* If no existing data, creates new object with provided data
* @param newData - Partial data to merge with existing single data
* @returns This instance for method chaining
*
* @example
* ```typescript
* const storage = new ComponentDataStorage<User>();
* storage.updateSingleData({ id: 1, name: 'John', email: 'john@example.com' });
* storage.patchSingleData({ email: 'john.doe@example.com' });
* // Result: { id: 1, name: 'John', email: 'john.doe@example.com' }
* ```
*/
patchSingleData(newData: Partial<T>): this;
/**
* Replaces the entire multiple data array
* @param newData - New array of data to replace existing data
* @returns This instance for method chaining
*/
updateMultipleData(newData: Array<T>): this;
/**
* Replaces the single data object
* @param newData - New data object or null to replace existing data
* @returns This instance for method chaining
*/
updateSingleData(newData: T | null): this;
/**
* Adds a single item to the multiple data array
* @param item - Single item to add to the array
* @returns This instance for method chaining
*/
addToMultipleData(item: T): this;
/**
* Removes an item from the multiple data array based on a predicate function
* @param predicate - Function that returns true for items to remove
* @returns This instance for method chaining
*
* @example
* ```typescript
* storage.removeFromMultipleData(user => user.id === 1);
* ```
*/
removeFromMultipleData(predicate: (item: T) => boolean): this;
/**
* Updates an item in the multiple data array based on a predicate function
* @param predicate - Function that returns true for items to update
* @param updateFn - Function that returns the updated item
* @returns This instance for method chaining
*
* @example
* ```typescript
* storage.updateItemInMultipleData(
* user => user.id === 1,
* user => ({ ...user, name: 'Updated Name' })
* );
* ```
*/
updateItemInMultipleData(predicate: (item: T) => boolean, updateFn: (item: T) => T): this;
/**
* Clears all data (both single and multiple)
* @returns This instance for method chaining
*/
clearAll(): this;
/**
* Clears only the single data
* @returns This instance for method chaining
*/
clearSingleData(): this;
/**
* Clears only the multiple data
* @returns This instance for method chaining
*/
clearMultipleData(): this;
/**
* Checks if single data exists (is not null)
* @returns true if single data exists, false otherwise
*/
hasSingleData(): boolean;
/**
* Checks if multiple data has items
* @returns true if multiple data array has items, false if empty
*/
hasMultipleData(): boolean;
/**
* Gets the count of items in multiple data
* @returns Number of items in the multiple data array
*/
getMultipleDataCount(): number;
/**
* Finds an item in the multiple data array
* @param predicate - Function that returns true for the item to find
* @returns The found item or undefined
*/
findInMultipleData(predicate: (item: T) => boolean): T | undefined;
/**
* Checks if an item exists in the multiple data array
* @param predicate - Function that returns true for the item to check
* @returns true if item exists, false otherwise
*/
existsInMultipleData(predicate: (item: T) => boolean): boolean;
}
/**
* Configuration options for NgSelectHelper
*/
type NgSelectHelperOpts = {
ajaxUrl: string;
httpClient: HttpClient;
destroyRef: DestroyRef;
usePostRequest: boolean;
limit?: number;
useCache?: boolean;
skipLoadingSpinner?: boolean;
};
/**
* Reset options for NgSelectHelper
*/
type NgSelectHelperResetOpts = {
resetQueryParams: boolean;
resetBody: boolean;
resetCache: boolean;
};
/**
* Query parameter type for NgSelectHelper
*/
type NgSelectHelperQueryParam = Record<string, string | number | boolean>;
/**
* Body type for NgSelectHelper POST requests
*/
type NgSelectHelperBody = Array<string | number | boolean | null | Record<string, string | number | boolean | null>> | Record<string, string | number | boolean | null | Array<string | number | boolean | null | Record<string, string | number | boolean | null>>>;
/**
* Paged data response specifically for ng-select
*/
declare class NgSelectPagedDataResponse<TData> {
readonly payload: Array<TData>;
readonly totalCount: number;
constructor(payload: Array<TData>, totalCount: number);
}
/**
* Zod schema for validating ng-select paged response
*/
declare const NgSelectPagedDataResponseZodSchema: z.ZodObject<{
payload: z.ZodArray<z.ZodAny, "many">;
totalCount: z.ZodNumber;
}, "strip", z.ZodTypeAny, {
payload: any[];
totalCount: number;
}, {
payload: any[];
totalCount: number;
}>;
/**
* NgSelectHelper class for managing ng-select components with HTTP data loading,
* pagination, caching, and search functionality using Angular signals
*
* @template TData The type of data items in the select options
*
* @example
* ```typescript
* interface User {
* id: number;
* name: string;
* email: string;
* }
*
* @Component({
* selector: 'app-user-select',
* template: `
* <ng-select
* [items]="selectHelper.loadedData().payload"
* bindLabel="name"
* bindValue="id"
* [loading]="selectHelper.isLoading()"
* [typeahead]="selectHelper.inputSubject"
* (open)="selectHelper.onOpen()"
* (close)="selectHelper.onClose()"
* (clear)="selectHelper.onClear()"
* (scrollToEnd)="selectHelper.onScrollToEnd()">
* </ng-select>
* `
* })
* export class UserSelectComponent implements OnInit {
* private httpClient = inject(HttpClient);
* private destroyRef = inject(DestroyRef);
*
* selectHelper = NgSelectHelper.create<User>({
* ajaxUrl: '/api/users',
* httpClient: this.httpClient,
* destroyRef: this.destroyRef,
* usePostRequest: false,
* limit: 20
* });
*
* ngOnInit() {
* this.selectHelper.init();
* }
* }
* ```
*/
declare class NgSelectHelper<TData> {
#private;
private ajaxUrl;
private readonly httpClient;
private readonly destroyRef;
readonly usePostRequest: boolean;
private readonly useCache;
private skipLoadingSpinner;
constructor(ajaxUrl: string, httpClient: HttpClient, destroyRef: DestroyRef, usePostRequest?: boolean, limit?: number, useCache?: boolean, skipLoadingSpinner?: boolean);
readonly inputSubject: Subject<string>;
readonly ajaxError$: Observable<Error>;
readonly loadedData: _angular_core.Signal<NgSelectPagedDataResponse<TData>>;
readonly isLoading: _angular_core.Signal<boolean>;
private runningApiReq;
/**
* Creates a new instance of NgSelectHelper
* @param options Configuration options
* @returns New NgSelectHelper instance
*/
static create<T>({ ajaxUrl, httpClient, destroyRef, usePostRequest, limit, useCache, skipLoadingSpinner }: NgSelectHelperOpts): NgSelectHelper<T>;
/**
* Sets whether to skip the loading spinner for HTTP requests
* @param skip Whether to skip the loading spinner
* @returns This instance for method chaining
*/
setSkipLoadingSpinner(skip: boolean): this;
/**
* Sets the debounce time for search input in seconds
* @param debounceTimeInSecond Debounce time in seconds
* @returns This instance for method chaining
*/
setDebounceTimeInSecond(debounceTimeInSecond: number): this;
/**
* Patches the request body (only works with POST requests)
* @param value Body data to merge
* @returns This instance for method chaining
*/
patchBody(value: NgSelectHelperBody): this;
/**
* Sets the request body (only works with POST requests)
* @param newBody New body data
* @returns This instance for method chaining
*/
setBody(newBody: NgSelectHelperBody): this;
/**
* Clears the internal cache
* @returns This instance for method chaining
*/
clearCache(): this;
/**
* Sets route parameters for the URL
* @param newRouteParam Route parameter to append
* @returns This instance for method chaining
*/
setRouteParam(newRouteParam: string): this;
/**
* Patches query parameters
* @param value Query parameters to merge
* @returns This instance for method chaining
*/
patchQueryParams(value: NgSelectHelperQueryParam): this;
/**
* Removes a query parameter
* @param key Query parameter key to remove
* @returns This instance for method chaining
*/
removeQueryParam(key: string): this;
/**
* Sets query parameters
* @param newQueryParams New query parameters
* @returns This instance for method chaining
*/
setQueryParams(newQueryParams: NgSelectHelperQueryParam): this;
/**
* Handler for ng-select blur event
*/
onBlur(): void;
/**
* Handler for ng-select close event
*/
onClose(): void;
/**
* Handler for ng-select clear event
*/
onClear(): Promise<void>;
/**
* Handler for ng-select open event
*/
onOpen(): void;
/**
* Handler for ng-select scroll to end event
*/
onScrollToEnd(): void;
/**
* Gets whether the last API call was successful
*/
get isLastApiCallSuccessful(): boolean;
/**
* Gets whether the limit has been reached
*/
get limitReached(): boolean;
/**
* Gets current query parameters
*/
get queryParams(): NgSelectHelperQueryParam;
/**
* Gets current request body
*/
get body(): NgSelectHelperBody;
/**
* Gets current page number
*/
get page(): number;
/**
* Gets total count of available items
*/
get totalCount(): number;
/**
* Gets whether initialization is complete
*/
get isInitDone(): boolean;
/**
* Initializes the NgSelectHelper with event handlers
* Should be called in ngOnInit or similar lifecycle method
*/
init(): void;
/**
* Loads data from the API
* @param page Page number
* @param limit Items per page
* @param searchText Search term
* @returns Observable of paged data response
*/
private loadDataFromApi;
/**
* Updates page counter if conditions are met
*/
private runProbablePageCounterUpdate;
/**
* Checks if the limit has been reached
*/
private runLimitReachedCheck;
/**
* Updates state after successful initial API call
* @param data Response data
*/
private updateStateOnSuccessfulInitialApiCall;
/**
* Updates state after successful subsequent API call
* @param data Response data
*/
private updateStateOnSuccessfulSubsequentApiCall;
/**
* Updates state after failed API call
*/
private updateStateOnFailedApiCall;
/**
* Resets all state to initial values
* @param opts Reset options
*/
resetAll(opts?: NgSelectHelperResetOpts): void;
/**
* Resets search text
*/
private resetSearchText;
}
/**
* Utility function to initialize NgSelect helpers with error handling
*
* This function streamlines the initialization process for multiple NgSelect helpers
* by automatically initializing them and setting up error handling subscriptions.
*
* @param helpers$ Observable of NgSelectHelper instances
* @param destroyRef Angular DestroyRef for automatic cleanup
* @param onAjaxError Callback function to handle