@ngrx/signals
Version:
Reactive Store and Set of Utilities for Angular Signals
361 lines (349 loc) • 11.4 kB
TypeScript
import { PartialStateUpdater, Prettify, SignalStoreFeatureResult, StateSignals, WritableStateSource, SignalStoreFeature, EmptyFeatureResult } from '@ngrx/signals';
import * as i0 from '@angular/core';
import { Type, Provider, Injector } from '@angular/core';
import { OperatorFunction, Observable } from 'rxjs';
type EventInstance<Type extends string, Payload> = {
type: Type;
payload: Payload;
};
type EventCreator<Type extends string, Payload> = ((payload: Payload) => EventInstance<Type, Payload>) & {
type: Type;
};
declare function event<Type extends string>(type: Type): EventCreator<Type, void>;
declare function event<Type extends string, Payload>(type: Type, payload: Payload): EventCreator<Type, Payload>;
type CaseReducerResult<State extends object, EventCreators extends EventCreator<string, any>[]> = {
reducer: CaseReducer<State, EventCreators>;
events: EventCreators;
};
type CaseReducer<State extends object, EventCreators extends EventCreator<string, any>[]> = (event: {
[K in keyof EventCreators]: ReturnType<EventCreators[K]>;
}[number], state: State) => Partial<State> | PartialStateUpdater<State> | Array<Partial<State> | PartialStateUpdater<State>>;
/**
* @description
*
* Creates a case reducer that can be used with the `withReducer` feature.
*/
declare function on<State extends object, EventCreators extends EventCreator<string, any>[]>(...args: [
...events: [...EventCreators],
reducer: CaseReducer<NoInfer<State>, NoInfer<EventCreators>>
]): CaseReducerResult<State, EventCreators>;
type EventScope = 'self' | 'parent' | 'global';
type EventScopeConfig = {
scope: EventScope;
};
/**
* @description
*
* Marks a single event to be dispatched in the specified scope.
* Used in a tuple alongside an event to indicate its dispatch scope.
*
* @usageNotes
*
* ```ts
* import { signalStore, type } from '@ngrx/signals';
* import { event, Events, withEffects } from '@ngrx/signals/events';
* import { mapResponse } from '@ngrx/operators';
*
* const opened = event('[Users Page] Opened');
* const loadedSuccess = event('[Users API] Loaded Success', type<User[]>());
* const loadedFailure = event('[Users API] Loaded Failure', type<string>());
*
* const UsersStore = signalStore(
* withEffects((
* _,
* events = inject(Events),
* usersService = inject(UsersService)
* ) => ({
* loadUsers$: events.on(opened).pipe(
* exhaustMap(() =>
* usersService.getAll().pipe(
* mapResponse({
* next: (users) => loadedSuccess(users),
* error: (error: { message: string }) => [
* loadedFailure(error.message),
* toScope('global'),
* ],
* }),
* ),
* ),
* ),
* })),
* );
* ```
*/
declare function toScope(scope: EventScope): EventScopeConfig;
/**
* @description
*
* RxJS operator that maps all emitted events in the stream to be dispatched
* in the specified scope.
*
* @usageNotes
*
* ```ts
* import { signalStore, type } from '@ngrx/signals';
* import { event, Events, withEffects } from '@ngrx/signals/events';
* import { mapResponse } from '@ngrx/operators';
*
* const opened = event('[Users Page] Opened');
* const loadedSuccess = event('[Users API] Loaded Success', type<User[]>());
* const loadedFailure = event('[Users API] Loaded Failure', type<string>());
*
* const UsersStore = signalStore(
* withEffects((
* _,
* events = inject(Events),
* usersService = inject(UsersService)
* ) => ({
* loadUsers$: events.on(opened).pipe(
* exhaustMap(() =>
* usersService.getAll().pipe(
* mapResponse({
* next: (users) => loadedSuccess(users),
* error: (error: { message: string }) =>
* loadedFailure(error.message),
* }),
* mapToScope('parent'),
* ),
* ),
* ),
* })),
* );
* ```
*/
declare function mapToScope<T extends EventInstance<string, unknown>>(scope: EventScope): OperatorFunction<T, [T, EventScopeConfig]>;
declare abstract class BaseEvents {
protected readonly events$: Observable<EventInstance<string, unknown>>;
protected constructor(parentEventsToken: Type<BaseEvents>);
on(): Observable<EventInstance<string, unknown>>;
on<EventCreators extends EventCreator<string, any>[]>(...events: [...EventCreators]): Observable<{
[K in keyof EventCreators]: ReturnType<EventCreators[K]>;
}[number]>;
}
/**
* @description
*
* Globally provided service for listening to dispatched events.
*
* @usageNotes
*
* ```ts
* import { event, Events } from '@ngrx/signals/events';
*
* const increment = event('[Counter Page] Increment');
*
* \@Component({ /* ... *\/ })
* class Counter {
* readonly #events = inject(Events);
*
* constructor() {
* this.#events
* .on(increment)
* .pipe(takeUntilDestroyed())
* .subscribe(() => /* handle increment event *\/);
* }
* }
* ```
*/
declare class Events extends BaseEvents {
constructor();
static ɵfac: i0.ɵɵFactoryDeclaration<Events, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<Events>;
}
/**
* @description
*
* Globally provided service for listening to dispatched events.
* Receives events before the `Events` service and is primarily used for
* handling state transitions.
*/
declare class ReducerEvents extends BaseEvents {
constructor();
static ɵfac: i0.ɵɵFactoryDeclaration<ReducerEvents, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<ReducerEvents>;
}
/**
* @description
*
* Globally provided service for dispatching events.
*
* @usageNotes
*
* ```ts
* import { Dispatcher, event } from '@ngrx/signals/events';
*
* const increment = event('[Counter Page] Increment');
*
* \@Component({ /* ... *\/ })
* class Counter {
* readonly #dispatcher = inject(Dispatcher);
*
* increment(): void {
* this.#dispatcher.dispatch(increment());
* }
* }
* ```
*/
declare class Dispatcher {
protected readonly reducerEvents: ReducerEvents;
protected readonly events: Events;
protected readonly parentDispatcher: Dispatcher | null;
dispatch(event: EventInstance<string, unknown>, config?: EventScopeConfig): void;
static ɵfac: i0.ɵɵFactoryDeclaration<Dispatcher, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<Dispatcher>;
}
/**
* @description
*
* Provides scoped instances of Dispatcher and Events services.
* Enables event dispatching within a specific component or feature scope.
*
* @usageNotes
*
* ```ts
* import { Dispatcher, event } from '@ngrx/signals/events';
*
* const increment = event('[Counter Page] Increment');
*
* \@Component({
* /* ... *\/
* providers: [provideDispatcher()],
* })
* class Counter {
* readonly #dispatcher = inject(Dispatcher);
*
* increment(): void {
* // Dispatching an event to the local Dispatcher.
* this.#dispatcher.dispatch(increment());
*
* // Dispatching an event to the parent Dispatcher.
* this.#dispatcher.dispatch(increment(), { scope: 'parent' });
*
* // Dispatching an event to the global Dispatcher.
* this.#dispatcher.dispatch(increment(), { scope: 'global' });
* }
* }
* ```
*/
declare function provideDispatcher(): Provider[];
type EventType<Source extends string, EventName extends string> = `[${Source}] ${EventName}`;
type EventCreatorGroup<Source extends string, Events extends Record<string, any>> = {
readonly [EventName in keyof Events]: EventName extends string ? EventCreator<EventType<Source, EventName>, Events[EventName]> : never;
};
/**
* @description
*
* Creates a group of event creators.
*
* @usageNotes
*
* ```ts
* import { type } from '@ngrx/signals';
* import { eventGroup } from '@ngrx/signals/events';
*
* const counterPageEvents = eventGroup({
* source: 'Counter Page',
* events: {
* increment: type<void>(),
* decrement: type<void>(),
* set: type<number>(),
* },
* });
* ```
*/
declare function eventGroup<Source extends string, Events extends Record<string, unknown>>(config: {
source: Source;
events: Events;
}): Prettify<EventCreatorGroup<Source, Events>>;
type SelfDispatchingEvents<EventGroup extends Record<string, EventCreator<string, any>>> = {
readonly [EventName in keyof EventGroup]: Parameters<EventGroup[EventName]> extends [infer Payload] ? (payload: Payload) => void : () => void;
};
/**
* @description
*
* Creates self-dispatching events for a given event group.
*
* @usageNotes
*
* ```ts
* import { type } from '@ngrx/signals';
* import { eventGroup, injectDispatch } from '@ngrx/signals/events';
*
* const counterPageEvents = eventGroup({
* source: 'Counter Page',
* events: {
* increment: type<void>(),
* decrement: type<void>(),
* },
* });
*
* \@Component({ /* ... *\/ })
* class Counter {
* readonly dispatch = injectDispatch(counterPageEvents);
*
* increment(): void {
* this.dispatch.increment();
* }
*
* decrement(): void {
* this.dispatch.decrement();
* }
* }
* ```
*/
declare function injectDispatch<EventGroup extends Record<string, EventCreator<string, any>>>(events: EventGroup, config?: {
injector?: Injector;
}): ((config: EventScopeConfig) => Prettify<SelfDispatchingEvents<EventGroup>>) & Prettify<SelfDispatchingEvents<EventGroup>>;
/**
* @description
*
* SignalStore feature for defining event handlers.
*
* @usageNotes
*
* ```ts
* import { signalStore, withState } from '@ngrx/signals';
* import { event, Events, withEventHandlers } from '@ngrx/signals/events';
*
* const increment = event('[Counter Page] Increment');
* const decrement = event('[Counter Page] Decrement');
*
* const CounterStore = signalStore(
* withState({ count: 0 }),
* withEventHandlers(({ count }, events = inject(Events)) => ({
* logCount$: events.on(increment, decrement).pipe(
* tap(({ type }) => console.log(type, count())),
* ),
* })),
* );
* ```
*/
declare function withEventHandlers<Input extends SignalStoreFeatureResult>(handlersFactory: (store: Prettify<StateSignals<Input['state']> & Input['props'] & Input['methods'] & WritableStateSource<Prettify<Input['state']>>>) => Record<string, Observable<unknown>> | Observable<unknown>[]): SignalStoreFeature<Input, EmptyFeatureResult>;
/**
* @description
*
* SignalStore feature for defining state transitions based on dispatched events.
*
* @usageNotes
*
* ```ts
* import { signalStore, type, withState } from '@ngrx/signals';
* import { event, on, withReducer } from '@ngrx/signals/events';
*
* const set = event('[Counter Page] Set', type<number>());
*
* const CounterStore = signalStore(
* withState({ count: 0 }),
* withReducer(
* on(set, ({ payload }) => ({ count: payload })),
* ),
* );
* ```
*/
declare function withReducer<State extends object>(...caseReducers: CaseReducerResult<State, EventCreator<string, any>[]>[]): SignalStoreFeature<{
state: State;
props: {};
methods: {};
}, EmptyFeatureResult>;
export { Dispatcher, Events, ReducerEvents, event, eventGroup, injectDispatch, mapToScope, on, provideDispatcher, toScope, withEventHandlers, withReducer };
export type { EventCreator, EventInstance, EventScope, EventScopeConfig };