@ngrx/signals 21.0.0 → 21.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"libs-version.js","sourceRoot":"","sources":["../../../../../modules/signals/schematics-core/utility/libs-version.ts"],"names":[],"mappings":";;;AAAa,QAAA,eAAe,GAAG,SAAS,CAAC","sourcesContent":["export const platformVersion = '^21.0.0';\n"]}
1
+ {"version":3,"file":"libs-version.js","sourceRoot":"","sources":["../../../../../modules/signals/schematics-core/utility/libs-version.ts"],"names":[],"mappings":";;;AAAa,QAAA,eAAe,GAAG,SAAS,CAAC","sourcesContent":["export const platformVersion = '^21.1.0';\n"]}
@@ -137,7 +137,7 @@ declare abstract class BaseEvents {
137
137
  *
138
138
  * const increment = event('[Counter Page] Increment');
139
139
  *
140
- * \@Component({ /* ... *\/ })
140
+ * \@Component(...)
141
141
  * class Counter {
142
142
  * readonly #events = inject(Events);
143
143
  *
@@ -180,7 +180,7 @@ declare class ReducerEvents extends BaseEvents {
180
180
  *
181
181
  * const increment = event('[Counter Page] Increment');
182
182
  *
183
- * \@Component({ /* ... *\/ })
183
+ * \@Component(...)
184
184
  * class Counter {
185
185
  * readonly #dispatcher = inject(Dispatcher);
186
186
  *
@@ -212,7 +212,7 @@ declare class Dispatcher {
212
212
  * const increment = event('[Counter Page] Increment');
213
213
  *
214
214
  * \@Component({
215
- * /* ... *\/
215
+ * // ...
216
216
  * providers: [provideDispatcher()],
217
217
  * })
218
218
  * class Counter {
@@ -285,7 +285,7 @@ type SelfDispatchingEvents<EventGroup extends Record<string, EventCreator<string
285
285
  * },
286
286
  * });
287
287
  *
288
- * \@Component({ /* ... *\/ })
288
+ * \@Component(...)
289
289
  * class Counter {
290
290
  * readonly dispatch = injectDispatch(counterPageEvents);
291
291
  *
@@ -7,6 +7,45 @@ type RxMethodRef = {
7
7
  type RxMethod<Input> = ((input: Input | (() => Input) | Observable<Input>, config?: {
8
8
  injector?: Injector;
9
9
  }) => RxMethodRef) & RxMethodRef;
10
+ /**
11
+ * @description
12
+ *
13
+ * Creates a reactive method for managing side effects by utilizing RxJS APIs.
14
+ * The method accepts an observable, a signal, a computation function, or
15
+ * a static value.
16
+ *
17
+ * @usageNotes
18
+ *
19
+ * ```ts
20
+ * import { Component, inject, signal } from '@angular/core';
21
+ * import { switchMap } from 'rxjs';
22
+ * import { rxMethod } from '@ngrx/signals/rxjs-interop';
23
+ * import { tapResponse } from '@ngrx/operators';
24
+ *
25
+ * \@Component(...)
26
+ * export class TodoList {
27
+ * readonly #todosService = inject(TodosService);
28
+ * readonly userId = signal(1);
29
+ * readonly todos = signal<Todo[]>([]);
30
+ *
31
+ * readonly loadTodos = rxMethod<number>(
32
+ * switchMap((id) =>
33
+ * this.#todosService.getByUserId(id).pipe(
34
+ * tapResponse({
35
+ * next: (todos) => this.todos.set(todos),
36
+ * error: console.error,
37
+ * })
38
+ * )
39
+ * )
40
+ * );
41
+ *
42
+ * constructor() {
43
+ * // 👇 Load todos on `userId` changes.
44
+ * this.loadTodos(this.userId);
45
+ * }
46
+ * }
47
+ * ```
48
+ */
10
49
  declare function rxMethod<Input>(generator: (source$: Observable<Input>) => Observable<unknown>, config?: {
11
50
  injector?: Injector;
12
51
  }): RxMethod<Input>;
@@ -1,6 +1,41 @@
1
1
  import { StateSource, Prettify, WritableStateSource } from '@ngrx/signals';
2
2
 
3
3
  type UnprotectedSource<Source extends StateSource<object>> = Source extends StateSource<infer State> ? Prettify<Omit<Source, keyof StateSource<State>> & WritableStateSource<State>> : never;
4
+ /**
5
+ * @description
6
+ *
7
+ * Allows updating the protected state of a SignalStore for testing purposes.
8
+ *
9
+ * @usageNotes
10
+ *
11
+ * ```ts
12
+ * import { TestBed } from '@angular/core/testing';
13
+ * import {
14
+ * patchState,
15
+ * signalStore,
16
+ * withState,
17
+ * withComputed,
18
+ * } from '@ngrx/signals';
19
+ * import { unprotected } from '@ngrx/signals/testing';
20
+ *
21
+ * const UserStore = signalStore(
22
+ * { providedIn: 'root' },
23
+ * withState({ firstName: 'Eric', lastName: 'Clapton' }),
24
+ * withComputed(({ firstName, lastName }) => ({
25
+ * fullName: () => `${firstName()} ${lastName()}`,
26
+ * }))
27
+ * );
28
+ *
29
+ * describe('UserStore', () => {
30
+ * it('recomputes fullName on firstName changes', () => {
31
+ * const userStore = TestBed.inject(UserStore);
32
+ *
33
+ * patchState(unprotected(userStore), { firstName: 'Patrick' });
34
+ * expect(userStore.fullName()).toBe('Patrick Clapton');
35
+ * });
36
+ * });
37
+ * ```
38
+ */
4
39
  declare function unprotected<Source extends StateSource<object>>(source: Source): UnprotectedSource<Source>;
5
40
 
6
41
  export { unprotected };
@@ -15,11 +15,64 @@ type DeepSignal<T> = Signal<T> & (IsKnownRecord<T> extends true ? Readonly<{
15
15
  [K in keyof T]: IsKnownRecord<T[K]> extends true ? DeepSignal<T[K]> : Signal<T[K]>;
16
16
  }> : unknown);
17
17
 
18
+ /**
19
+ * @description
20
+ *
21
+ * Creates a computed signal with deeply nested signals for each property when
22
+ * the result is an object literal.
23
+ *
24
+ * @usageNotes
25
+ *
26
+ * ```ts
27
+ * import { signal } from '@angular/core';
28
+ * import { deepComputed } from '@ngrx/signals';
29
+ *
30
+ * const limit = signal(10);
31
+ * const offset = signal(0);
32
+ *
33
+ * const pagination = deepComputed(() => ({
34
+ * currentPage: Math.floor(offset() / limit()) + 1,
35
+ * pageSize: limit(),
36
+ * }));
37
+ *
38
+ * console.log(pagination()); // { currentPage: 1, pageSize: 10 }
39
+ * console.log(pagination.currentPage()); // 1
40
+ * console.log(pagination.pageSize()); // 10
41
+ * ```
42
+ */
18
43
  declare function deepComputed<T extends object>(computation: () => T): DeepSignal<T>;
19
44
 
20
45
  type SignalMethod<Input> = ((input: Input | (() => Input), config?: {
21
46
  injector?: Injector;
22
47
  }) => EffectRef) & EffectRef;
48
+ /**
49
+ * @description
50
+ *
51
+ * Creates a method for managing side effects with signals.
52
+ * The method accepts a signal, a computation function, or a static value.
53
+ *
54
+ * @usageNotes
55
+ *
56
+ * ```ts
57
+ * import { Component, signal } from '@angular/core';
58
+ * import { signalMethod } from '@ngrx/signals';
59
+ *
60
+ * \@Component(...)
61
+ * export class Counter {
62
+ * readonly count = signal(1);
63
+ * readonly logDoubledNumber = signalMethod<number>(
64
+ * (num) => console.log(num * 2)
65
+ * );
66
+ *
67
+ * constructor() {
68
+ * this.logDoubledNumber(10); // logs: 20
69
+ *
70
+ * this.logDoubledNumber(this.count); // logs: 2
71
+ * setTimeout(() => this.count.set(2), 1_000); // logs: 4 (after 1s)
72
+ * }
73
+ * }
74
+ * ```
75
+ */
23
76
  declare function signalMethod<Input>(processingFn: (value: Input) => void, config?: {
24
77
  injector?: Injector;
25
78
  }): SignalMethod<Input>;
@@ -38,8 +91,84 @@ type StateSource<State extends object> = {
38
91
  type PartialStateUpdater<State extends object> = (state: State) => Partial<State>;
39
92
  type StateWatcher<State extends object> = (state: NoInfer<State>) => void;
40
93
  declare function isWritableStateSource<State extends object>(stateSource: StateSource<State>): stateSource is WritableStateSource<State>;
94
+ /**
95
+ * @description
96
+ *
97
+ * Updates the state of a SignalStore or SignalState.
98
+ * Accepts a sequence of partial state objects and partial state updaters.
99
+ *
100
+ * @usageNotes
101
+ *
102
+ * ```ts
103
+ * import { patchState, signalStore, withMethods, withState } from '@ngrx/signals';
104
+ *
105
+ * export const CounterStore = signalStore(
106
+ * withState({ count1: 0, count2: 0 }),
107
+ * withMethods((store) => ({
108
+ * incrementFirst(): void {
109
+ * patchState(store, (state) => ({ count1: state.count1 + 1 }));
110
+ * },
111
+ * resetSecond(): void {
112
+ * patchState(store, { count2: 0 });
113
+ * },
114
+ * }))
115
+ * );
116
+ * ```
117
+ */
41
118
  declare function patchState<State extends object>(stateSource: WritableStateSource<State>, ...updaters: Array<Partial<NoInfer<State>> | PartialStateUpdater<NoInfer<State>>>): void;
119
+ /**
120
+ * @description
121
+ *
122
+ * Returns a snapshot of the current state from a SignalStore or SignalState.
123
+ * When used within a reactive context, state changes are automatically tracked.
124
+ *
125
+ * @usageNotes
126
+ *
127
+ * ```ts
128
+ * import { Component, effect, inject } from '@angular/core';
129
+ * import { getState, signalStore, withState } from '@ngrx/signals';
130
+ *
131
+ * export const CounterStore = signalStore(
132
+ * withState({ count1: 0, count2: 0 })
133
+ * );
134
+ *
135
+ * \@Component(...)
136
+ * export class Counter {
137
+ * readonly store = inject(CounterStore);
138
+ *
139
+ * constructor() {
140
+ * effect(() => {
141
+ * const state = getState(this.store);
142
+ * // 👇 Logs on state changes.
143
+ * console.log(state);
144
+ * });
145
+ * }
146
+ * }
147
+ * ```
148
+ */
42
149
  declare function getState<State extends object>(stateSource: StateSource<State>): State;
150
+ /**
151
+ * @description
152
+ *
153
+ * Synchronously tracks state changes of a SignalStore or SignalState.
154
+ *
155
+ * @usageNotes
156
+ *
157
+ * ```ts
158
+ * import { Component } from '@angular/core';
159
+ * import { signalState, watchState } from '@ngrx/signals';
160
+ *
161
+ * \@Component(...)
162
+ * export class Counter {
163
+ * readonly state = signalState({ count1: 0, count2: 0 });
164
+ *
165
+ * constructor() {
166
+ * // 👇 Synchronously logs every state change without debouncing.
167
+ * watchState(this.state, console.log);
168
+ * }
169
+ * }
170
+ * ```
171
+ */
43
172
  declare function watchState<State extends object>(stateSource: StateSource<State>, watcher: StateWatcher<State>, config?: {
44
173
  injector?: Injector;
45
174
  }): {
@@ -47,6 +176,32 @@ declare function watchState<State extends object>(stateSource: StateSource<State
47
176
  };
48
177
 
49
178
  type SignalState<State extends object> = DeepSignal<State> & WritableStateSource<State>;
179
+ /**
180
+ * @description
181
+ *
182
+ * Creates a state container with deeply nested signals for each property that
183
+ * is an object literal.
184
+ *
185
+ * @usageNotes
186
+ *
187
+ * ```ts
188
+ * import { Component } from '@angular/core';
189
+ * import { signalState, patchState } from '@ngrx/signals';
190
+ *
191
+ * \@Component(...)
192
+ * export class Counter {
193
+ * readonly state = signalState({ count: 0 });
194
+ *
195
+ * logCount(): void {
196
+ * console.log(this.state.count());
197
+ * }
198
+ *
199
+ * increment(): void {
200
+ * patchState(this.state, ({ count }) => ({ count: count + 1 }));
201
+ * }
202
+ * }
203
+ * ```
204
+ */
50
205
  declare function signalState<State extends object>(initialState: State): SignalState<State>;
51
206
 
52
207
  type StateSignals<State> = IsKnownRecord<Prettify<State>> extends true ? {
@@ -215,6 +370,26 @@ declare function type<T>(): T;
215
370
  type ComputedResult<ComputedDictionary extends Record<string | symbol, Signal<unknown> | (() => unknown)>> = {
216
371
  [P in keyof ComputedDictionary]: ComputedDictionary[P] extends Signal<unknown> ? ComputedDictionary[P] : ComputedDictionary[P] extends () => infer V ? Signal<V> : never;
217
372
  };
373
+ /**
374
+ * @description
375
+ *
376
+ * Adds computed signals to a SignalStore.
377
+ * Accepts a factory function that returns a dictionary of computed signals or
378
+ * computation functions.
379
+ *
380
+ * @usageNotes
381
+ *
382
+ * ```ts
383
+ * import { signalStore, withState, withComputed } from '@ngrx/signals';
384
+ *
385
+ * export const CounterStore = signalStore(
386
+ * withState({ count: 0 }),
387
+ * withComputed(({ count }) => ({
388
+ * doubleCount: () => count() * 2,
389
+ * }))
390
+ * );
391
+ * ```
392
+ */
218
393
  declare function withComputed<Input extends SignalStoreFeatureResult, ComputedDictionary extends Record<string | symbol, Signal<unknown> | (() => unknown)>>(computedFactory: (store: Prettify<StateSignals<Input['state']> & Input['props'] & Input['methods']>) => ComputedDictionary): SignalStoreFeature<Input, {
219
394
  state: {};
220
395
  props: ComputedResult<ComputedDictionary>;
@@ -223,25 +398,27 @@ declare function withComputed<Input extends SignalStoreFeatureResult, ComputedDi
223
398
 
224
399
  /**
225
400
  * @description
226
- * Allows passing properties, methods, or signals from a SignalStore
227
- * to a feature.
401
+ *
402
+ * Allows passing state signals, properties, and methods from a SignalStore
403
+ * instance to a custom feature.
228
404
  *
229
405
  * @usageNotes
230
- * ```typescript
231
- * signalStore(
406
+ *
407
+ * ```ts
408
+ * import { signalStore, withFeature, withMethods } from '@ngrx/signals';
409
+ *
410
+ * export const UserStore = signalStore(
232
411
  * withMethods((store) => ({
233
- * load(id: number): Observable<Entity> {
234
- * return of({ id, name: 'John' });
412
+ * loadById(id: number): Promise<User> {
413
+ * return Promise.resolve({ id, name: 'John' });
235
414
  * },
236
415
  * })),
237
416
  * withFeature(
238
- * // 👇 has full access to the store
239
- * (store) => withEntityLoader((id) => firstValueFrom(store.load(id)))
417
+ * // 👇 Has full access to store members.
418
+ * (store) => withEntityLoader((id) => store.loadById(id))
240
419
  * )
241
420
  * );
242
421
  * ```
243
- *
244
- * @param featureFactory function returning the actual feature
245
422
  */
246
423
  declare function withFeature<Input extends SignalStoreFeatureResult, Output extends SignalStoreFeatureResult>(featureFactory: (store: Prettify<StateSignals<Input['state']> & Input['props'] & Input['methods'] & WritableStateSource<Input['state']>>) => SignalStoreFeature<Input, Output>): SignalStoreFeature<Input, Output>;
247
424
 
@@ -263,11 +440,17 @@ type LinkedStateResult<LinkedStateInput extends Record<string | symbol, Writable
263
440
  * @description
264
441
  *
265
442
  * Adds linked state slices to a SignalStore.
443
+ * Accepts a factory function that returns a dictionary of linked signals or
444
+ * computation functions.
266
445
  *
267
446
  * @usageNotes
268
447
  *
269
- * ```typescript
270
- * const OptionsStore = signalStore(
448
+ * ### Using a computation function
449
+ *
450
+ * ```ts
451
+ * import { signalStore, withLinkedState, withState } from '@ngrx/signals';
452
+ *
453
+ * export const OptionsStore = signalStore(
271
454
  * withState({ options: [1, 2, 3] }),
272
455
  * withLinkedState(({ options }) => ({
273
456
  * selectedOption: () => options()[0],
@@ -275,15 +458,15 @@ type LinkedStateResult<LinkedStateInput extends Record<string | symbol, Writable
275
458
  * );
276
459
  * ```
277
460
  *
278
- * This returns a state of type `{ options: number[], selectedOption: number | undefined }`.
279
- * When the `options` signal changes, the `selectedOption` automatically updates.
461
+ * ### Using linkedSignal for advanced use cases
280
462
  *
281
- * For advanced use cases, `linkedSignal` or any other `WritableSignal` instance can be used within `withLinkedState`:
463
+ * ```ts
464
+ * import { linkedSignal } from '@angular/core';
465
+ * import { signalStore, withLinkedState, withState } from '@ngrx/signals';
282
466
  *
283
- * ```typescript
284
467
  * type Option = { id: number; label: string };
285
468
  *
286
- * const OptionsStore = signalStore(
469
+ * export const OptionsStore = signalStore(
287
470
  * withState({ options: [] as Option[] }),
288
471
  * withLinkedState(({ options }) => ({
289
472
  * selectedOption: linkedSignal<Option[], Option>({
@@ -292,12 +475,10 @@ type LinkedStateResult<LinkedStateInput extends Record<string | symbol, Writable
292
475
  * const option = newOptions.find((o) => o.id === previous?.value.id);
293
476
  * return option ?? newOptions[0];
294
477
  * },
295
- * })
478
+ * }),
296
479
  * }))
297
480
  * )
298
481
  * ```
299
- *
300
- * @param linkedStateFactory A function that returns an object literal with properties containing an actual `linkedSignal` or the computation function.
301
482
  */
302
483
  declare function withLinkedState<State extends Record<string | symbol, WritableSignal<unknown> | (() => unknown)>, Input extends SignalStoreFeatureResult>(linkedStateFactory: (store: Prettify<StateSignals<Input['state']> & Input['props']>) => State): SignalStoreFeature<Input, {
303
484
  state: LinkedStateResult<State>;
@@ -305,12 +486,54 @@ declare function withLinkedState<State extends Record<string | symbol, WritableS
305
486
  methods: {};
306
487
  }>;
307
488
 
489
+ /**
490
+ * @description
491
+ *
492
+ * Adds methods to a SignalStore.
493
+ *
494
+ * @usageNotes
495
+ *
496
+ * ```ts
497
+ * import { patchState, signalStore, withMethods, withState } from '@ngrx/signals';
498
+ *
499
+ * export const CounterStore = signalStore(
500
+ * withState({ count: 0 }),
501
+ * withMethods((store) => ({
502
+ * increment(): void {
503
+ * patchState(store, ({ count }) => ({ count: count + 1 }));
504
+ * },
505
+ * decrement(): void {
506
+ * patchState(store, ({ count }) => ({ count: count - 1 }));
507
+ * },
508
+ * }))
509
+ * );
510
+ * ```
511
+ */
308
512
  declare function withMethods<Input extends SignalStoreFeatureResult, Methods extends MethodsDictionary>(methodsFactory: (store: Prettify<StateSignals<Input['state']> & Input['props'] & Input['methods'] & WritableStateSource<Input['state']>>) => Methods): SignalStoreFeature<Input, {
309
513
  state: {};
310
514
  props: {};
311
515
  methods: Methods;
312
516
  }>;
313
517
 
518
+ /**
519
+ * @description
520
+ *
521
+ * Adds custom properties to a SignalStore.
522
+ *
523
+ * @usageNotes
524
+ *
525
+ * ```ts
526
+ * import { toObservable } from '@angular/core/rxjs-interop';
527
+ * import { signalStore, withProps, withState } from '@ngrx/signals';
528
+ *
529
+ * export const TodosStore = signalStore(
530
+ * withState({ todos: [] as Todo[], isLoading: false }),
531
+ * withProps(({ isLoading }) => ({
532
+ * isLoading$: toObservable(isLoading),
533
+ * }))
534
+ * );
535
+ * ```
536
+ */
314
537
  declare function withProps<Input extends SignalStoreFeatureResult, Props extends object>(propsFactory: (store: Prettify<StateSignals<Input['state']> & Input['props'] & Input['methods'] & WritableStateSource<Input['state']>>) => Props): SignalStoreFeature<Input, {
315
538
  state: {};
316
539
  props: Props;