UNPKG

@ngrx/signals

Version:

Reactive Store and Set of Utilities for Angular Signals

361 lines (349 loc) 11.4 kB
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 };