import * as _ngrx_signals from '@ngrx/signals';
import { SignalStoreFeature, SignalStoreFeatureResult, Prettify, StateSignals, WritableStateSource, DeepSignal, EmptyFeatureResult } from '@ngrx/signals';
import * as _angular_core from '@angular/core';
import { Signal, OnDestroy, Provider } from '@angular/core';
import { SelectEntityId, EntityState, EntityProps, NamedEntityState, NamedEntityProps } from '@ngrx/signals/entities';
import { Observable, Subscription } from 'rxjs';
import { CollectionViewer } from '@angular/cdk/collections';
import { Params } from '@angular/router';
import * as _ngrx_traits_signals from '@ngrx-traits/signals';
type FeatureConfigFactory, FactoryConfig extends Record = Config> = Config | ((store: StoreSource) => FactoryConfig);
type StoreSource = Prettify & Input['props'] & Input['methods'] & WritableStateSource>;
declare function getFeatureConfig>(config: FeatureConfigFactory, store: StoreSource): Config;
type ExtractStoreFeatureOutput SignalStoreFeature> = ReturnType extends SignalStoreFeature ? In & Out : never;
type CallStatus$1 = 'init' | 'loading' | 'loaded' | {
error: unknown;
};
type CallStatusState = {
callStatus: CallStatus$1;
};
type CallStatusComputed = {
isLoading: Signal;
} & {
isLoaded: Signal;
} & {
error: Signal;
};
type CallStatusMethods = {
setLoading: () => void;
} & {
setLoaded: () => void;
} & {
setError: (error?: Error) => void;
};
type NamedCallStatusState = {
[K in Prop as `${K}CallStatus`]: CallStatus$1;
};
type NamedCallStatusComputed = {
[K in Prop as `is${Capitalize}Loading`]: Signal;
} & {
[K in Prop as `is${Capitalize}Loaded`]: Signal;
} & {
[K in Prop as `${K}Error`]: Signal;
};
type NamedCallStatusMethods = {
[K in Prop as `set${Capitalize}Loading`]: () => void;
} & {
[K in Prop as `set${Capitalize}Loaded`]: () => void;
} & {
[K in Prop as `set${Capitalize}Error`]: (error?: Error) => void;
};
/**
* Generates necessary state, computed and methods for call progress status to the store
* @param configFactory - The configuration object for the feature or a factory function that receives the store and returns the configuration object
* @param configFactory.prop - The name of the property for which this represents the call status
* @param configFactory.initialValue - The initial value of the call status
* @param configFactory.collection - The name of the collection for which this represents the call status is an alias to prop param
* @param configFactory.errorType - The type of the error
* they do the same thing
*
* prop or collection is required
* @example
* const store = signalStore(
* withCallStatus({ collection: 'user', })
* // other valid configurations
* // withCallStatus()
* // withCallStatus({ collection: 'user', initialValue: 'loading' , errorType: type()})
* )
*
* // generates the following signals
* store.userEntitiesCallStatus // 'init' | 'loading' | 'loaded' | { error: unknown }
* // generates the following computed signals
* store.isUserEntitiesLoading // boolean
* store.isUserEntitiesLoaded // boolean
* store.userEntitiesError // unknown | null
* // generates the following methods
* store.setUserEntitiesLoading // () => void
* store.setUserEntitiesLoaded // () => void
* store.setUserEntitiesError // (error?: unknown) => void
*/
declare function withCallStatus(configFactory: FeatureConfigFactory): SignalStoreFeature;
props: NamedCallStatusComputed<`${Prop}Entities`, Error>;
methods: NamedCallStatusMethods<`${Prop}Entities`, Error>;
}>;
declare function withCallStatus(configFactory: FeatureConfigFactory): SignalStoreFeature;
props: NamedCallStatusComputed;
methods: NamedCallStatusMethods;
}>;
declare function withCallStatus(configFactory?: FeatureConfigFactory): SignalStoreFeature;
methods: CallStatusMethods;
}>;
type ObservableCall$1 = (() => Observable) | ((arg: Param) => Observable);
type PromiseCall$1 = (() => Promise) | ((arg: Param) => Promise);
type Call$1 = ObservableCall$1 | PromiseCall$1;
type CallConfig = {
/**
* The main function to be called.
*/
call: Call$1;
/**
* The name of the property where the result of the call will be stored.
*/
resultProp: PropName;
/**
* Specifies how to map emissions of the call, using one of the following:
* - 'switchMap': Cancels the previous call when a new one starts.
* - 'concatMap': Queues calls and executes them sequentially.
* - 'exhaustMap': Ignores new calls until the current one completes.
* Default is exhaustMap
*/
mapPipe?: 'switchMap' | 'concatMap' | 'exhaustMap';
/**
* default is true, if false disables automatically storing the result of the
* function, and removes the generated types.
*/
storeResult?: boolean;
/**
* A default value for the result before the call is executed
*/
defaultResult?: NoInfer;
/**
* Callback function invoked on successful completion of the call.
* Receives the result of the call and the parameter used.
*/
onSuccess?: (result: NoInfer, param: NoInfer, previousResult: NoInfer | undefined) => void;
/**
* A function to transform an error from the call into a custom `Error` type.
* Receives the error and the parameter used.
*/
mapError?: (error: unknown, param: NoInfer) => Error;
/**
* Callback function invoked if the call encounters an error.
* Receives the mapped error and the parameter used.
*/
onError?: (error: Error, param: NoInfer) => void;
/**
* A function with condition that determines whether the call should be skipped.
* The function accepts the call parameter and must return a boolean | Observable.
*/
skipWhen?: (param: NoInfer, previousResult: NoInfer | undefined) => boolean | Promise | Observable;
/**
* Reactively execute the call with the provided params.
* Supports the following:
* - A direct parameter value. Which execute the call once on init.
* - A function or `Observable` emitting the parameter of the call or undefined.
* - A function returning the parameter or undefined.
*
* **Warning**: By default, when withCall is a function, signal
* or observable that when returns a falsy value it will skip the call.
* To override this behavior, define a skipWhen with your own rule or skipWhen: () => false
* to always execute on any value.
*/
callWith?: Param extends undefined ? Observable | (() => boolean) | boolean : NoInfer | null | undefined | Observable> | (() => NoInfer | null | undefined);
};
type ExtractCallResultPropName = T extends CallConfig ? T['storeResult'] extends false ? never : T['resultProp'] extends '' ? `${K & string}Result` : T['resultProp'] & string : `${K & string}Result`;
type ExtractCallResultType = T extends Call$1 ? R | undefined : T extends CallConfig ? D extends undefined ? R | undefined : D : never;
type ExtractErrorType = T extends CallConfig ? E : unknown;
type NamedCallsStatusComputed> = {
[K in keyof Calls as K extends `_${infer J}` ? `_is${Capitalize}Loading` : `is${Capitalize}Loading`]: Signal;
} & {
[K in keyof Calls as K extends `_${infer J}` ? `_is${Capitalize}Loaded` : `is${Capitalize}Loaded`]: Signal;
} & {
[K in keyof Calls as `${K & string}Error`]: Calls[K] extends CallConfig ? Signal : Signal;
};
type RxMethodRef = {
destroy: () => void;
};
type EntitiesFilterState = {
entitiesFilter: Filter;
};
type EntitiesFilterComputed = {
isEntitiesFilterChanged: Signal;
};
type NamedEntitiesFilterState = {
[K in Collection as `${K}EntitiesFilter`]: Filter;
};
type NamedEntitiesFilterComputed = {
[K in Collection as `is${Capitalize}EntitiesFilterChanged`]: Signal;
};
type FilterOptions = Filter | {
filter: Filter;
debounce?: number;
patch?: false | undefined;
forceLoad?: boolean;
} | {
filter: Partial;
debounce?: number;
patch: true;
forceLoad?: boolean;
};
type EntitiesFilterMethods = {
filterEntities: {
(options?: (() => FilterOptions) | Observable>): RxMethodRef;
(options?: FilterOptions): Promise<{
value: Signal;
ok: true;
} | {
error: Signal;
ok: false;
}>;
};
resetEntitiesFilter: (options?: {
newDefaultFilter?: Filter;
debounce?: number;
forceLoad?: boolean;
skipLoadingCall?: boolean;
}) => void;
};
type NamedEntitiesFilterMethods = {
[K in Collection as `filter${Capitalize}Entities`]: {
(options?: (() => FilterOptions) | Observable>): RxMethodRef;
(options?: FilterOptions): Promise<{
value: Signal;
ok: true;
} | {
error: Signal;
ok: false;
}>;
};
} & {
[K in Collection as `reset${Capitalize}EntitiesFilter`]: (options?: {
newDefaultFilter?: Filter;
debounce?: number;
forceLoad?: boolean;
skipLoadingCall?: boolean;
}) => void;
};
/**
* Generates necessary state, computed and methods for locally filtering entities in the store,
* the generated filter[Collection]Entities method will filter the entities based on the filter function
* and is debounced by default.
*
* Requires withEntities to be used.
*
* @param configFactory - The configuration object for the feature or a factory function that receives the store and returns the configuration object
* @param configFactory.filterFn - The function that will be used to filter the entities
* @param configFactory.defaultFilter - The default filter to be used
* @param configFactory.defaultDebounce - The default debounce time to be used, if not set it will default to 300ms
* @param configFactory.entity - The entity type to be used
* @param configFactory.collection - The optional collection name to be used
* @param configFactory.selectId - The function to use to select the id of the entity
*
* @example
* const entity = type();
* const collection = 'product';
* const store = signalStore(
* { providedIn: 'root' },
* // requires withEntities to be used
* withEntities({ entity, collection }),
*
* withEntitiesLocalFilter({
* entity,
* collection,
* defaultFilter: { search: '' },
* filterFn: (entity, filter) =>
* !filter?.search || // if there is no search term return all entities
* entity?.name.toLowerCase().includes(filter?.search.toLowerCase()),
* }),
* );
*
* // generates the following signals
* store.productEntitiesFilter // { search: string }
* // generates the following methods
* store.filterProductEntities // (options: { filter: { search: string }, debounce?: number, patch?: boolean, forceLoad?: boolean }) => void
* store.resetProductEntitiesFilter // (options?: { newDefaultFilter?: { search: string } }) => void — resets to defaultFilter or to newDefaultFilter if provided, updating the default for future resets
*/
declare function withEntitiesLocalFilter, Collection extends string = ''>(configFactory: FeatureConfigFactory boolean;
defaultFilter: Filter;
defaultDebounce?: number;
entity: Entity;
collection?: Collection;
selectId?: SelectEntityId;
}>): SignalStoreFeature;
props: EntityProps;
methods: {};
} : {
state: NamedEntityState;
props: NamedEntityProps;
methods: {};
}), Collection extends '' ? {
state: EntitiesFilterState;
props: EntitiesFilterComputed;
methods: EntitiesFilterMethods;
} : {
state: NamedEntitiesFilterState;
props: NamedEntitiesFilterComputed;
methods: NamedEntitiesFilterMethods;
}>;
type EntitiesRemoteFilterMethods = {
filterEntities: {
(options?: Observable & {
skipLoadingCall?: boolean;
}> | (() => FilterOptions)): RxMethodRef;
(options?: FilterOptions & {
skipLoadingCall?: boolean;
}): Promise<{
value: Signal;
ok: true;
} | {
error: Signal;
ok: false;
}>;
};
resetEntitiesFilter: (options?: {
newDefaultFilter?: Filter;
debounce?: number;
forceLoad?: boolean;
skipLoadingCall?: boolean;
}) => void;
};
type NamedEntitiesRemoteFilterMethods = {
[K in Collection as `filter${Capitalize}Entities`]: {
(options?: Observable & {
skipLoadingCall?: boolean;
}> | (() => FilterOptions)): RxMethodRef;
(options?: FilterOptions & {
skipLoadingCall?: boolean;
}): Promise<{
value: Signal;
ok: true;
} | {
error: Signal;
ok: false;
}>;
};
} & {
[K in Collection as `reset${Capitalize}EntitiesFilter`]: (options?: {
newDefaultFilter?: Filter;
debounce?: number;
forceLoad?: boolean;
skipLoadingCall?: boolean;
}) => void;
};
/**
* Generates necessary state, computed and methods for remotely filtering entities in the store,
* the generated filter[Collection]Entities method will filter the entities by calling set[Collection]Loading()
* and you should either create an effect that listens to [Collection]Loading can call the api with the [Collection]Filter params
* or use withEntitiesLoadingCall to call the api with the [Collection]Filter params.
* filter[Collection]Entities is debounced by default, you can change the debounce by using the debounce option filter[Collection]Entities or changing the defaultDebounce prop in the config.
*
* In case you dont want filter[Collection]Entities to call set[Collection]Loading() (which triggers a fetchEntities), you can pass skipLoadingCall: true to filter[Collection]Entities.
* Useful in cases where you want to further change the state before manually calling set[Collection]Loading() to trigger a fetch of entities.
*
* Requires withEntities and withCallStatus to be present before this function.
* @param configFactory - The configuration object for the feature or a factory function that receives the store and returns the configuration object
* @param configFactory.defaultFilter - The default filter to be used
* @param configFactory.defaultDebounce - The default debounce time to be used, if not set it will default to 300ms
* @param configFactory.entity - The entity type to be used
* @param configFactory.collection - The optional collection name to be used
*
* @example
* const entity = type();
* const collection = 'product';
* export const store = signalStore(
* // requires withEntities and withCallStatus to be used
* withEntities({ entity, collection }),
* withCallStatus({ collection, initialValue: 'loading' }),
*
* withEntitiesRemoteFilter({
* entity,
* collection,
* defaultFilter: { search: '' },
* }),
* // after you can use withEntitiesLoadingCall to connect the filter to
* // the api call, or do it manually as shown after
* withEntitiesLoadingCall({
* collection,
* fetchEntities: ({ productEntitiesFilter }) => {
* return inject(ProductService)
* .getProducts({
* search: productEntitiesFilter().search,
* })
* },
* }),
* // withEntitiesLoadingCall is the same as doing the following:
* // withHooks(({ isProductEntitiesLoading, productEntitiesFilter, setProductEntitiesError, ...state }) => ({
* // onInit: async () => {
* // effect(() => {
* // if (isProductEntitiesLoading()) {
* // inject(ProductService)
* // .getProducts({
* // search: productEntitiesFilter().search,
* // })
* // .pipe(
* // takeUntilDestroyed(),
* // tap((res) =>
* // patchState(
* // state,
* // setAllEntities(res.resultList, { collection: 'product' }),
* // ),
* // ),
* // catchError((error) => {
* // setProductEntitiesError(error);
* // return EMPTY;
* // }),
* // )
* // .subscribe();
* // }
* // });
* // },
* })),
* // generates the following signals
* store.productEntitiesFilter // { search: string }
* // generates the following methods
* store.filterProductEntities // (options: { filter: { search: string }, debounce?: number, patch?: boolean, forceLoad?: boolean, skipLoadingCall?:boolean }) => void
* store.resetProductEntitiesFilter // (options?: { newDefaultFilter?: { search: string } }) => void — resets to defaultFilter or to newDefaultFilter if provided, updating the default for future resets
*/
declare function withEntitiesRemoteFilter, Collection extends string = ''>(configFactory: FeatureConfigFactory): SignalStoreFeature;
props: EntityProps;
methods: CallStatusMethods;
} : {
state: NamedEntityState;
props: NamedEntityProps;
methods: NamedCallStatusMethods<`${Collection}Entities`>;
}), Collection extends '' ? {
state: EntitiesFilterState;
props: EntitiesFilterComputed;
methods: EntitiesRemoteFilterMethods;
} : {
state: NamedEntitiesFilterState;
props: NamedEntitiesFilterComputed;
methods: NamedEntitiesRemoteFilterMethods;
}>;
/**
* Generates necessary state and methods to do remote and local filtering of entities in the store,
* the generated filter[Collection]Entities method will filter the entities by calling set[Collection]Loading() if the isRemoteFilter returns true
* and if false will call the filterFn to filter the entities locally.
*
* For the remote case you should either create an effect that listens to [Collection]Loading can call the api with the [Collection]Filter params
* or use withEntitiesLoadingCall to call the api with the [Collection]Filter params. filter[Collection]Entities
* is debounced by default, you can change the debounce by using the debounce option filter[Collection]Entities or changing the defaultDebounce prop in the config.
*
* In case you dont want filter[Collection]Entities to call set[Collection]Loading() (which triggers a fetchEntities), you can pass skipLoadingCall: true to filter[Collection]Entities.
* Useful in cases where you want to further change the state before manually calling set[Collection]Loading() to trigger a fetch of entities.
*
* Requires withEntities and withCallStatus to be present before this function.
* @param configFactory - The configuration object for the feature or a factory function that receives the store and returns the configuration object
* @param configFactory.defaultFilter - The default filter to be used
* @param configFactory.defaultDebounce - The default debounce time to be used, if not set it will default to 300ms
* @param configFactory.filterFn - The function to filter the entities
* @param configFactory.isRemoteFilter - The function to determine if the filter is remote or local
* @param configFactory.entity - The entity type to be used
* @param configFactory.collection - The optional collection name to be used
* @param configFactory.selectId - The optional function to select the id of the entity
*
* @example
* const entity = type();
* const collection = 'product';
* export const store = signalStore(
* { providedIn: 'root' },
* // requires withEntities and withCallStatus to be used
* withEntities({ entity, collection }),
* withCallStatus({ collection, initialValue: 'loading' }),
* withEntitiesHybridFilter({
* entity,
* collection,
* defaultFilter: { search: '' , category: ''},
* filterFn: (entity, filter) =>
* (!filter.search || entity.name.toLowerCase().includes(filter.search.toLowerCase()))
* // in this case the filter will call setProductEntitiesLoading() if the category changes, othewise
* // it will filter the entities locally using filterFn
* isRemoteFilter: (previous, current) => {
* return previous.category !== current.category;
* }
* }),
* // after you can use withEntitiesLoadingCall to connect the filter to
* // the api call, or do it manually as shown after
* withEntitiesLoadingCall({
* collection,
* fetchEntities: ({ productEntitiesFilter }) => {
* return inject(ProductService)
* .getProducts({
* category: productEntitiesFilter().category,
* })
* },
* }),
* // withEntitiesLoadingCall is the same as doing the following:
* // withHooks(({ productEntitiesCallStatus, setProductEntitiesError, ...state }) => ({
* // onInit: async () => {
* // effect(() => {
* // if (isProductEntitiesLoading()) {
* // inject(ProductService)
* // .getProducts({
* // category: productEntitiesFilter().category,
* // })
* // .pipe(
* // takeUntilDestroyed(),
* // tap((res) =>
* // patchState(
* // state,
* // setAllEntities(res.resultList, { collection: 'product' }),
* // ),
* // ),
* // catchError((error) => {
* // setProductEntitiesError(error);
* // return EMPTY;
* // }),
* // )
* // .subscribe();
* // }
* // });
* // },
* })),
* // generates the following signals
* store.productEntitiesFilter // { search: string , category: string }
* // generates the following methods
* store.filterProductEntities // (options: { filter: { search: string, category: string }, debounce?: number, patch?: boolean, forceLoad?: boolean, skipLoadingCall?:boolean }) => void
* store.resetProductEntitiesFilter // (options?: { newDefaultFilter?: { search: string, category: string } }) => void — resets to defaultFilter or to newDefaultFilter if provided, updating the default for future resets
*/
declare function withEntitiesHybridFilter, Collection extends string = ''>(configFactory: FeatureConfigFactory;
filterFn: (entity: NoInfer, filter: NoInfer) => boolean;
isRemoteFilter: (previous: NoInfer, current: NoInfer) => boolean;
}>): SignalStoreFeature;
props: EntityProps;
methods: CallStatusMethods;
} : {
state: NamedEntityState;
props: NamedEntityProps;
methods: NamedCallStatusMethods<`${Collection}Entities`>;
}), Collection extends '' ? {
state: EntitiesFilterState;
props: EntitiesFilterComputed;
methods: EntitiesRemoteFilterMethods;
} : {
state: NamedEntitiesFilterState;
props: NamedEntitiesFilterComputed;
methods: NamedEntitiesRemoteFilterMethods;
}>;
type EntitiesPaginationLocalState = {
entitiesPagination: {
currentPage: number;
pageSize: number;
};
};
type NamedEntitiesPaginationLocalState = {
[K in Collection as `${K}EntitiesPagination`]: {
currentPage: number;
pageSize: number;
};
};
type EntitiesPaginationLocalComputed = {
entitiesCurrentPage: DeepSignal<{
entities: Entity[];
pageIndex: number;
total: number | undefined;
pageSize: number;
pagesCount: number | undefined;
hasPrevious: boolean;
hasNext: boolean;
}>;
};
type NamedEntitiesPaginationLocalComputed = {
[K in Collection as `${K}EntitiesCurrentPage`]: DeepSignal<{
entities: Entity[];
pageIndex: number;
total: number | undefined;
pageSize: number;
pagesCount: number | undefined;
hasPrevious: boolean;
hasNext: boolean;
}>;
};
type EntitiesPaginationLocalMethods = {
loadEntitiesPage: (options: {
pageIndex: number;
pageSize?: number;
}) => void;
};
type NamedEntitiesPaginationLocalMethods = {
[K in Collection as `load${Capitalize}EntitiesPage`]: (options: {
pageIndex: number;
pageSize?: number;
}) => void;
};
type SetEntitiesResult = {
setEntitiesPagedResult: (result: ResultParam) => void;
};
type NamedSetEntitiesResult = {
[K in Collection as `set${Capitalize}EntitiesPagedResult`]: (result: ResultParam) => void;
};
/**
* Generates necessary state, computed and methods for local pagination of entities in the store.
*
* Requires withEntities to be present in the store.
* @param configFactory - The configuration object for the feature or a factory function that receives the store and returns the configuration object
* @param configFactory.pageSize - The number of entities to show per page
* @param configFactory.currentPage - The current page to show
* @param configFactory.entity - The entity type
* @param configFactory.collection - The name of the collection
*
* @example
* const entity = type();
* const collection = "product";
* export const ProductsLocalStore = signalStore(
* { providedIn: 'root' },
* // required withEntities
* withEntities({ entity, collection }),
* withEntitiesLocalPagination({
* entity,
* collection,
* pageSize: 5,
* }),
*
* // generates the following signals
* store.productEntitiesPagination // { currentPage: 0, pageSize: 5 }
* // generates the following computed signals
* store.productEntitiesCurrentPage // { entities: Product[], pageIndex: 0, total: 10, pageSize: 5, pagesCount: 2, hasPrevious: false, hasNext: true }
* // generates the following methods
* store.loadProductEntitiesPage // ({ pageIndex: number }) => void
*/
declare function withEntitiesLocalPagination(configFactory: FeatureConfigFactory): SignalStoreFeature;
props: EntityProps;
methods: {};
} : {
state: NamedEntityState;
props: NamedEntityProps;
methods: {};
}), Collection extends '' ? {
state: EntitiesPaginationLocalState;
props: EntitiesPaginationLocalComputed;
methods: EntitiesPaginationLocalMethods;
} : {
state: NamedEntitiesPaginationLocalState;
props: NamedEntitiesPaginationLocalComputed;
methods: NamedEntitiesPaginationLocalMethods;
}>;
type PaginationState = {
currentPage: number;
requestPage: number;
pageSize: number;
total: number;
pagesToCache: number;
cache: {
start: number;
end: number;
};
};
type EntitiesPaginationRemoteState = {
entitiesPagination: PaginationState;
};
type NamedEntitiesPaginationRemoteState = {
[K in Collection as `${K}EntitiesPagination`]: PaginationState;
};
type EntitiesPaginationRemoteComputed = {
entitiesCurrentPage: DeepSignal<{
entities: Entity[];
pageIndex: number;
total: number | undefined;
pageSize: number;
pagesCount: number | undefined;
hasPrevious: boolean;
hasNext: boolean;
isLoading: boolean;
}>;
entitiesPagedRequest: DeepSignal<{
startIndex: number;
size: number;
page: number;
}>;
};
type NamedEntitiesPaginationRemoteComputed = {
[K in Collection as `${K}EntitiesPagedRequest`]: DeepSignal<{
startIndex: number;
size: number;
page: number;
}>;
} & {
[K in Collection as `${K}EntitiesCurrentPage`]: DeepSignal<{
entities: Entity[];
pageIndex: number;
total: number | undefined;
pageSize: number;
pagesCount: number | undefined;
hasPrevious: boolean;
hasNext: boolean;
isLoading: boolean;
}>;
};
type EntitiesPaginationRemoteMethods = {
loadEntitiesPage: (options: {
pageIndex: number;
pageSize?: number;
skipLoadingCall?: boolean;
forceLoad?: boolean;
}) => void;
} & SetEntitiesResult<{
entities: Entity[];
total: number;
}>;
type NamedEntitiesPaginationRemoteMethods = {
[K in Collection as `load${Capitalize}EntitiesPage`]: (options: {
pageIndex: number;
pageSize?: number;
skipLoadingCall?: boolean;
forceLoad?: boolean;
}) => void;
} & NamedSetEntitiesResult;
/**
* Generates necessary state, computed and methods for remote pagination of entities in the store.
* Call load[Collection]Page to change the page, it will try to load the new page from cache if it's not present,
* it will call set[Collection]Loading(), and you should either create an effect that listens to [Collection]Loading
* and call the api with the [Collection]PagedRequest params and use set[Collection]Result to set the result
* and changing the status errors manually,
* or use withEntitiesLoadingCall to call the api with the [Collection]PagedRequest params which handles setting
* the result and errors automatically.
*
* In case you dont want load[Collection]Page to call set[Collection]Loading() (which triggers a fetchEntities), you can pass skipLoadingCall: true to load[Collection]Page.
* Useful in cases where you want to further change the state before manually calling set[Collection]Loading() to trigger a fetch of entities.
*
* This will keep at least the provided (pagesToCache) pages in memory, so previous pages could be removed from the cache.
* If you need to keep all previous pages in memory, use withEntitiesRemoteScrollPagination instead.
*
* Requires withEntities and withCallStatus to be present in the store.
* @param configFactory - The configuration object for the feature or a factory function that receives the store and returns the configuration object
* @param configFactory.pageSize - The number of entities to show per page
* @param configFactory.currentPage - The current page to show
* @param configFactory.pagesToCache - The number of pages to cache
* @param configFactory.entity - The entity type
* @param configFactory.collection - The name of the collection
* @param configFactory.selectId - The function to use to select the id of the entity
*
* @example
* const entity = type();
* const collection = "product";
* export const store = signalStore(
* { providedIn: 'root' },
* // required withEntities and withCallStatus
* withEntities({ entity, collection }),
* withCallStatus({ prop: collection, initialValue: 'loading' }),
*
* withEntitiesRemotePagination({
* entity,
* collection,
* pageSize: 5,
* pagesToCache: 2,
* })
* // after you can use withEntitiesLoadingCall to connect the filter to
* // the api call, or do it manually as shown after
* withEntitiesLoadingCall({
* collection,
* fetchEntities: ({ productPagedRequest }) => {
* return inject(ProductService)
* .getProducts({
* take: productPagedRequest().size,
* skip: productPagedRequest().startIndex,
* }).pipe(
* map((d) => ({
* entities: d.resultList,
* total: d.total,
* })),
* )
* },
* }),
* // withEntitiesLoadingCall is the same as doing the following:
* // withHooks(({ productsLoading, setProductEntitiesError, setProductEntitiesPagedResult, ...state }) => ({
* // onInit: async () => {
* // effect(() => {
* // if (isProductEntitiesLoading()) {
* // inject(ProductService)
* // .getProducts({
* // take: productPagedRequest().size,
* // skip: productPagedRequest().startIndex,
* // })
* // .pipe(
* // takeUntilDestroyed(),
* // tap((res) =>
* // patchState(
* // state,
* // setProductEntitiesPagedResult({ entities: res.resultList, total: res.total } ),
* // ),
* // ),
* // catchError((error) => {
* // setProductEntitiesError(error);
* // return EMPTY;
* // }),
* // )
* // .subscribe();
* // }
* // });
* // },
* })),
* // generates the following signals
* store.productEntitiesPagination // { currentPage: number, requestPage: number, pageSize: 5, total: number, pagesToCache: number, cache: { start: number, end: number } } used internally
* // generates the following computed signals
* store.productEntitiesCurrentPage // { entities: Product[], pageIndex: number, total: number, pageSize: 5, pagesCount: number, hasPrevious: boolean, hasNext: boolean, isLoading: boolean }
* store.productPagedRequest // { startIndex: number, size: number, page: number }
* // generates the following methods
* store.loadProductEntitiesPage({ pageIndex: number, forceLoad?: boolean, skipLoadingCall?:boolean }) // loads the page and sets the requestPage to the pageIndex
* store.setProductEntitiesPagedResult(entities: Product[], total: number) // appends the entities to the cache of entities and total
*/
declare function withEntitiesRemotePagination(configFactory: FeatureConfigFactory;
}>): SignalStoreFeature;
props: EntityProps & CallStatusComputed;
methods: CallStatusMethods;
} : {
state: NamedEntityState;
props: NamedEntityProps & NamedCallStatusComputed<`${Collection}Entities`>;
methods: NamedCallStatusMethods<`${Collection}Entities`>;
}), Collection extends '' ? {
state: EntitiesPaginationRemoteState;
props: EntitiesPaginationRemoteComputed;
methods: EntitiesPaginationRemoteMethods;
} : {
state: NamedEntitiesPaginationRemoteState;
props: NamedEntitiesPaginationRemoteComputed;
methods: NamedEntitiesPaginationRemoteMethods;
}>;
type ScrollPaginationState = {
hasMore: boolean;
pageSize: number;
pagesToCache: number;
currentPage: number;
requestPage: number;
};
type EntitiesScrollPaginationState = {
pagination: ScrollPaginationState;
};
type NamedEntitiesScrollPaginationState = {
[K in Collection as `${K}EntitiesPagination`]: ScrollPaginationState;
};
type EntitiesScrollPaginationComputed = {
entitiesCurrentPage: DeepSignal<{
entities: Entity[];
pageIndex: number;
pageSize: number;
hasPrevious: boolean;
hasNext: boolean;
isLoading: boolean;
}>;
entitiesPagedRequest: DeepSignal<{
startIndex: number;
size: number;
}>;
};
type NamedEntitiesScrollPaginationComputed = {
[K in Collection as `${K}EntitiesPagedRequest`]: DeepSignal<{
startIndex: number;
size: number;
}>;
} & {
[K in Collection as `${K}EntitiesCurrentPage`]: DeepSignal<{
entities: Entity[];
pageIndex: number;
pageSize: number;
hasPrevious: boolean;
hasNext: boolean;
isLoading: boolean;
}>;
};
type EntitiesScrollPaginationMethods = SetEntitiesResult<{
entities: Entity[];
total: number;
} | {
entities: Entity[];
hasMore: boolean;
} | {
entities: Entity[];
}> & {
loadMoreEntities: () => void;
loadEntitiesNextPage: () => void;
loadEntitiesPreviousPage: () => void;
loadEntitiesFirstPage: () => void;
};
type NamedEntitiesScrollPaginationMethods = NamedSetEntitiesResult & {
[K in Collection as `loadMore${Capitalize}Entities`]: () => void;
} & {
[K in Collection as `load${Capitalize}EntitiesNextPage`]: () => void;
} & {
[K in Collection as `load${Capitalize}EntitiesPreviousPage`]: () => void;
} & {
[K in Collection as `load${Capitalize}EntitiesFirstPage`]: () => void;
};
/**
* Generates necessary state, computed and methods for remote infinite scroll pagination of entities in the store.
* This is ideal for implementing infinite scroll where the entities cache keeps growing, or for a paginated list that only
* allows going to the next and previous page because you dont know the total number of entities
* probably because the data is top big and partitioned in multiple nodes.
*
* When the page changes, it will try to load the current page from cache if it's not present,
* it will call set[Collection]Loading(), and you should either create an effect that listens to is[Collection]Loading
* and call the api with the [Collection]PagedRequest params and use set[Collection]Result to set the result
* and changing the status errors manually
* or use withEntitiesLoadingCall to call the api with the [Collection]PagedRequest params which handles setting
* the result and errors automatically. Requires withEntities and withCallStatus to be used.
*
* The generated set[Collection]Result method will append the entities to the cache of entities,
* it requires either just set of requested entities set[Collection]Result({ entities }) in which case it will assume there is no more result if you set less entities
* than the requested buffer size, or you can provide an extra param to the entities, total set[Collection]Result({ entities, total }) so it calculates if there is more
* or a hasMore param set[Collection]Result({entities, hasMore}) that you can set to false to indicate the end of the entities.
*
* Requires withEntities and withCallStatus to be present in the store.
* @param configFactory - The configuration object for the feature or a factory function that receives the store and returns the configuration object
* @param configFactory.pageSize - The number of entities to show per page
* @param configFactory.pagesToCache - The number of pages to cache
* @param configFactory.entity - The entity type
* @param configFactory.collection - The name of the collection
*
* @example
* const entity = type();
* const collection = 'product';
* export const store = signalStore(
* { providedIn: 'root' },
* // required withEntities and withCallStatus
* withEntities({ entity, collection }),
* withCallStatus({ collection, initialValue: 'loading' }),
*
* withEntitiesRemoteScrollPagination({
* entity,
* collection,
* pageSize: 5,
* pagesToCache: 2,
* })
* // after you can use withEntitiesLoadingCall to connect the filter to
* // the api call, or do it manually as shown after
* withEntitiesLoadingCall({
* collection,
* fetchEntities: ({ productPagedRequest }) => {
* return inject(ProductService)
* .getProducts({
* take: productPagedRequest().size,
* skip: productPagedRequest().startIndex,
* }).pipe(
* map((d) => ({
* entities: d.resultList,
* total: d.total,
* })),
* )
* },
* }),
* // withEntitiesLoadingCall is the same as doing the following:
* // withHooks(({ productEntitiesCallStatus, setProductEntitiesError, setProductPagedResult, ...state }) => ({
* // onInit: async () => {
* // effect(() => {
* // if (isProductEntitiesLoading()) {
* // inject(ProductService)
* // .getProducts({
* // take: productPagedRequest().size,
* // skip: productPagedRequest().startIndex,
* // })
* // .pipe(
* // takeUntilDestroyed(),
* // tap((res) =>
* // // total is not required, you can use hasMore or none see docs
* // setProductPagedResult({ entities: res.resultList, total: res.total } )
* // ),
* // catchError((error) => {
* // setProductEntitiesError(error);
* // return EMPTY;
* // }),
* // )
* // .subscribe();
* // }
* // });
* // },
* })),
*
* // in your component add
* store = inject(ProductsRemoteStore);
* dataSource = getInfiniteScrollDataSource(store, { collection: 'product' }) // pass this to your cdkVirtualFor see examples section
* // generates the following signals
* store.productEntitiesPagination // { currentPage: number, pageSize: number, pagesToCache: number, hasMore: boolean } used internally
* // generates the following computed signals
* store.productEntitiesCurrentPage // { entities: Entity[], pageIndex: number, total: number, pageSize: number, hasPrevious: boolean, hasNext: boolean, isLoading: boolean }
* store.productEntitiesPagedRequest // { startIndex: number, size: number }
* // generates the following methods
* store.loadProductEntitiesNextPage() // loads next page
* store.loadProductEntitiesPreviousPage() // loads previous page
* store.loadProductEntitiesFirstPage() // loads first page
* store.loadMoreProductEntities() // loads more entities (used for infinite scroll datasource)
* store.setProductEntitiesPagedResult(entities: Product[], total: number) // appends the entities to the cache of entities and total
*/
declare function withEntitiesRemoteScrollPagination(configFactory: FeatureConfigFactory;
}>): SignalStoreFeature;
props: EntityProps & CallStatusComputed;
methods: CallStatusMethods;
} : {
state: NamedEntityState;
props: NamedEntityProps & NamedCallStatusComputed<`${Collection}Entities`>;
methods: NamedCallStatusMethods<`${Collection}Entities`>;
}), Collection extends '' ? {
state: EntitiesScrollPaginationState;
props: EntitiesScrollPaginationComputed;
methods: EntitiesScrollPaginationMethods;
} : {
state: NamedEntitiesScrollPaginationState;
props: NamedEntitiesScrollPaginationComputed;
methods: NamedEntitiesScrollPaginationMethods;
}>;
declare function getInfiniteScrollDataSource(options: {
store: EntityProps & EntitiesScrollPaginationMethods;
debounceLoadMoreTime?: number;
} | {
collection: Collection;
entity: Entity;
store: NamedEntityProps & NamedEntitiesScrollPaginationMethods;
debounceLoadMoreTime?: number;
}): {
subscription?: Subscription;
entitiesList: Observable;
connect(collectionViewer: CollectionViewer): Observable;
disconnect(): void;
};
type SortDirection = 'asc' | 'desc' | '';
type Sort = {
/** The id of the column being sorted. */
field: keyof Entity | (string & {});
/** The sort direction. */
direction: SortDirection;
};
type CdkSort = {
/** The id of the column being sorted. */
active: keyof Entity | (string & {});
/** The sort direction. */
direction: SortDirection;
};
type EntitiesSortState = {
entitiesSort: Sort;
};
type NamedEntitiesSortState = {
[K in Collection as `${K}EntitiesSort`]: Sort;
};
type EntitiesSortMethods = {
sortEntities: (options?: Sort | CdkSort | {
sort: Sort | CdkSort;
} | Observable | CdkSort | {
sort: Sort | CdkSort;
}> | (() => Sort | CdkSort | {
sort: Sort | CdkSort;
})) => void;
};
type NamedEntitiesSortMethods = {
[K in Collection as `sort${Capitalize}Entities`]: (options?: Sort | CdkSort | {
sort: Sort | CdkSort;
} | Observable | CdkSort | {
sort: Sort | CdkSort;
}> | (() => Sort | CdkSort | {
sort: Sort | CdkSort;
})) => void;
};
type QueryMapper = Record> = {
/**
* @param firstLoad true only for the first query params emission restored
* into this store instance. Mappers should only read it, the caller resets
* it once all mappers have run.
*/
queryParamsToState: (query: T, store: Store, firstLoad: boolean) => void;
stateToQueryParams: (store: Store) => Signal | undefined | null;
};
/**
* @internal
* @ignore
* Gets a sorted copy of the data array based on the state of the Sort.
* @param data The array of data that should be sorted.
* @param sort The connected MatSort that holds the current sort state.
*/
declare function sortData(data: T[], sort: Sort): T[];
/**
* Generates necessary state, computed and methods for sorting locally entities in the store.
*
* Requires withEntities to be present before this function
* @param configFactory - The configuration object for the feature or a factory function that receives the store and returns the configuration object
* @param configFactory.defaultSort - The default sort to be applied to the entities
* @param configFactory.entity - The type entity to be used
* @param configFactory.collection - The name of the collection for which will be sorted
* @param configFactory.selectId - The function to use to select the id of the entity
* @param configFactory.sortFunction - Optional custom function use to sort the entities
*
* @example
* const entity = type();
* const collection = "product";
* export const store = signalStore(
* { providedIn: 'root' },
* withEntities({ entity, collection }),
* withEntitiesLocalSort({
* entity,
* collection,
* defaultSort: { field: 'name', direction: 'asc' },
* }),
* );
* // generates the following signals
* store.productEntitiesSort - the current sort applied to the products
* // generates the following methods
* store.sortProductEntities({ sort: { field: 'name', direction: 'asc' } }) - sorts the products entities
*/
declare function withEntitiesLocalSort(configFactory: FeatureConfigFactory>;
entity: Entity;
collection?: Collection;
selectId?: SelectEntityId;
sortFunction?: (entities: Entity[], sort: Sort) => Entity[];
}>): SignalStoreFeature;
props: EntityProps;
methods: {};
} : {
state: NamedEntityState;
props: NamedEntityProps;
methods: {};
}), Collection extends '' ? {
state: EntitiesSortState;
props: {};
methods: EntitiesSortMethods;
} : {
state: NamedEntitiesSortState;
props: {};
methods: NamedEntitiesSortMethods;
}>;
type EntitiesRemoteSortMethods = {
sortEntities: (options?: Sort | CdkSort | {
sort: Sort | CdkSort;
skipLoadingCall?: boolean;
} | Observable | CdkSort | {
sort: Sort | CdkSort;
skipLoadingCall?: boolean;
}> | (() => Sort | CdkSort | {
sort: Sort | CdkSort;
skipLoadingCall?: boolean;
})) => void;
};
type NamedEntitiesRemoteSortMethods = {
[K in Collection as `sort${Capitalize}Entities`]: (options?: Sort | CdkSort | {
sort: Sort | CdkSort;
skipLoadingCall?: boolean;
} | Observable | CdkSort | {
sort: Sort