@dvirus-js/angular-signals 0.0.1
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.
|
@@ -0,0 +1,1560 @@
|
|
|
1
|
+
import { Signal, WritableSignal, DestroyRef } from '@angular/core';
|
|
2
|
+
import { AbstractControl, FormControlStatus, ValidationErrors, FormArray, FormGroup, FormControl } from '@angular/forms';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Represents a generic object with string keys and values of any type.
|
|
6
|
+
* Useful as a base type for utility functions and signal helpers.
|
|
7
|
+
*
|
|
8
|
+
* @typedef {Record<string, any>} ObjectType
|
|
9
|
+
*/
|
|
10
|
+
type ObjectType = Record<string, any>;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Represents an object where each property is a read-only Signal.
|
|
14
|
+
* Useful for mapping state objects to their signal equivalents.
|
|
15
|
+
*/
|
|
16
|
+
type SignalObj<T extends ObjectType = ObjectType> = {
|
|
17
|
+
[Key in keyof T]: Signal<T[Key]>;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Represents an object where each property is a WritableSignal.
|
|
21
|
+
* Allows for direct modification of the signal values within the object structure.
|
|
22
|
+
*/
|
|
23
|
+
type SignalObjWritable<T extends ObjectType> = {
|
|
24
|
+
[Key in keyof T]: WritableSignal<T[Key]>;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* A type representing either a Signal of type T or the raw value of type T.
|
|
28
|
+
* Useful for inputs that can accept both static values and reactive signals.
|
|
29
|
+
*/
|
|
30
|
+
type SignalOrValue<T> = Signal<T> | T;
|
|
31
|
+
/**
|
|
32
|
+
* Represents an object where each property can be either a Signal of a certain type
|
|
33
|
+
* or the raw value of that type.
|
|
34
|
+
*
|
|
35
|
+
* @template TValues The type of the values contained within the SignalOrValue properties.
|
|
36
|
+
* Defaults to a union of ObjectType, string, number, or undefined.
|
|
37
|
+
*/
|
|
38
|
+
type SignalOrValueObj<TValues = ObjectType | string | number | boolean | undefined> = TValues extends object ? {
|
|
39
|
+
[Key in keyof TValues]: SignalOrValue<TValues[Key]>;
|
|
40
|
+
} : Record<string, SignalOrValue<TValues>>;
|
|
41
|
+
/**
|
|
42
|
+
* Converts a plain object into a writable signal object.
|
|
43
|
+
* Each property of the source object becomes a WritableSignal initialized with the property's value.
|
|
44
|
+
*
|
|
45
|
+
* @param src - The source object to convert.
|
|
46
|
+
* @returns An object with the same keys as the source, but with values wrapped in WritableSignals.
|
|
47
|
+
*/
|
|
48
|
+
declare function toSignalObj<T extends ObjectType>(src: T): SignalObjWritable<T>;
|
|
49
|
+
/**
|
|
50
|
+
* Converts a signal object into a plain object.
|
|
51
|
+
* Each property of the signal object becomes a value from the signal.
|
|
52
|
+
*
|
|
53
|
+
* @param src - The signal object to convert.
|
|
54
|
+
* @returns An object with the same keys as the source, but with values from the signals.
|
|
55
|
+
*/
|
|
56
|
+
declare function fromSignalObj<TData extends ObjectType>(src: SignalOrValueObj<TData>): TData;
|
|
57
|
+
/**
|
|
58
|
+
* Unwraps a value that might be a Signal.
|
|
59
|
+
* If the input is a Signal, it returns the signal's value.
|
|
60
|
+
* If the input is a raw value, it returns the value itself.
|
|
61
|
+
*
|
|
62
|
+
* @param value - The signal or value to unwrap.
|
|
63
|
+
* @returns The raw value of type T.
|
|
64
|
+
*/
|
|
65
|
+
declare function signalOrValue<T>(value: SignalOrValue<T>): T;
|
|
66
|
+
declare function signalOrFunction<T, FnArgs extends any[] = []>(value: SignalOrValue<T> | ((...args: FnArgs) => T), ...fnArgs: FnArgs): T;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Recursively extracts the value type from a form structure.
|
|
70
|
+
*
|
|
71
|
+
* Converts form control types to their underlying value types:
|
|
72
|
+
* - Arrays become arrays of extracted values
|
|
73
|
+
* - Objects become objects with extracted property values
|
|
74
|
+
* - Primitives become `T | undefined`
|
|
75
|
+
*
|
|
76
|
+
* @template T - The form structure type to extract values from
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* type Person = { name: string; age: number };
|
|
81
|
+
* type PersonValue = SignalFormValueFor<Person>;
|
|
82
|
+
* // Result: { name: string | undefined; age: number | undefined }
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
type SignalFormValueFor<T> = T extends (infer U)[] ? SignalFormValueFor<U>[] : T extends object ? {
|
|
86
|
+
[Key in keyof T]: SignalFormValueFor<T[Key]>;
|
|
87
|
+
} : T | undefined;
|
|
88
|
+
/**
|
|
89
|
+
* Recursively defines the error structure for a form.
|
|
90
|
+
*
|
|
91
|
+
* Mirrors the form structure where:
|
|
92
|
+
* - Arrays become arrays of error structures
|
|
93
|
+
* - Objects become objects with error properties
|
|
94
|
+
* - Primitives become error maps (Record<string, string>) or undefined
|
|
95
|
+
*
|
|
96
|
+
* @template T - The form structure type to create error structure for
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* type Person = { name: string; hobbies: string[] };
|
|
101
|
+
* type PersonErrors = SignalFormErrorFor<Person>;
|
|
102
|
+
* // Result: { name: Record<string, string> | undefined; hobbies: (Record<string, string> | undefined)[] }
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
type SignalFormErrorFor<T> = T extends (infer U)[] ? SignalFormErrorFor<U>[] : T extends object ? {
|
|
106
|
+
[Key in keyof T]: SignalFormErrorFor<T[Key]>;
|
|
107
|
+
} : Record<string, string> | undefined;
|
|
108
|
+
/**
|
|
109
|
+
* Represents the first error or warning found in a control.
|
|
110
|
+
*
|
|
111
|
+
* @template Type - Either 'error' or 'warning' to distinguish validation type
|
|
112
|
+
*
|
|
113
|
+
* @property name - The validator name that triggered this error/warning
|
|
114
|
+
* @property message - The human-readable error/warning message
|
|
115
|
+
* @property type - Whether this is an 'error' or 'warning'
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* const firstError: FirstError<'error'> = {
|
|
120
|
+
* name: 'required',
|
|
121
|
+
* message: 'This field is required',
|
|
122
|
+
* type: 'error'
|
|
123
|
+
* };
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
type FirstError<Type extends 'warning' | 'error'> = {
|
|
127
|
+
name: string;
|
|
128
|
+
message: string;
|
|
129
|
+
type: Type;
|
|
130
|
+
} | undefined;
|
|
131
|
+
/**
|
|
132
|
+
* Represents a single form control (primitive value) with validation and state management.
|
|
133
|
+
*
|
|
134
|
+
* A control wraps a primitive value (string, number, etc.) and provides:
|
|
135
|
+
* - Reactive value through Angular signals
|
|
136
|
+
* - Validation with errors and warnings
|
|
137
|
+
* - State tracking (touched, dirty, disabled)
|
|
138
|
+
* - Manual error/warning management
|
|
139
|
+
*
|
|
140
|
+
* @template TValue - The type of value this control manages
|
|
141
|
+
*/
|
|
142
|
+
interface SignalFormControl<TValue> {
|
|
143
|
+
/** Discriminator property set to 'control' for type checking */
|
|
144
|
+
kind: 'control';
|
|
145
|
+
/** Writable signal containing the current control value */
|
|
146
|
+
value: WritableSignal<TValue | undefined>;
|
|
147
|
+
/** Signal indicating if the control is disabled */
|
|
148
|
+
disabled: Signal<boolean>;
|
|
149
|
+
/** Signal indicating if the control has been interacted with */
|
|
150
|
+
touched: Signal<boolean>;
|
|
151
|
+
/** Signal indicating if the value has changed from initial */
|
|
152
|
+
dirty: Signal<boolean>;
|
|
153
|
+
/** Signal containing all validation errors (from validators + manual) */
|
|
154
|
+
errors: Signal<Record<string, string>>;
|
|
155
|
+
/** Signal containing all validation warnings (from warnings + manual) */
|
|
156
|
+
warnings: Signal<Record<string, string>>;
|
|
157
|
+
/** Signal containing only manually set errors */
|
|
158
|
+
selfErrors: Signal<Record<string, string>>;
|
|
159
|
+
/** Signal containing only manually set warnings */
|
|
160
|
+
selfWarnings: Signal<Record<string, string>>;
|
|
161
|
+
/** Signal true when control has errors and is not disabled */
|
|
162
|
+
invalid: Signal<boolean>;
|
|
163
|
+
/** Signal true when control has no errors */
|
|
164
|
+
valid: Signal<boolean>;
|
|
165
|
+
/** Signal with the first error, if any */
|
|
166
|
+
firstError: Signal<FirstError<'error'>>;
|
|
167
|
+
/** Signal with the first warning, if any */
|
|
168
|
+
firstWarning: Signal<FirstError<'warning'>>;
|
|
169
|
+
/** Signal with the first error or warning */
|
|
170
|
+
firstErrorOrWarning: Signal<FirstError<'error' | 'warning'>>;
|
|
171
|
+
/**
|
|
172
|
+
* Updates the control value with optional state flags.
|
|
173
|
+
* @param value - New value to set
|
|
174
|
+
* @param options - Optional flags for marking dirty/touched
|
|
175
|
+
*/
|
|
176
|
+
setValue: (value: TValue | undefined, options?: SignalFormSetValueOptions) => void;
|
|
177
|
+
/**
|
|
178
|
+
* Resets control to initial value and clears all state.
|
|
179
|
+
* @param value - Optional new initial value
|
|
180
|
+
*/
|
|
181
|
+
reset: (value?: TValue | undefined) => void;
|
|
182
|
+
/** Marks the control as touched */
|
|
183
|
+
markTouched: () => void;
|
|
184
|
+
/** Marks the control as not touched */
|
|
185
|
+
markUntouched: () => void;
|
|
186
|
+
/** Marks the control as dirty (modified) */
|
|
187
|
+
markDirty: () => void;
|
|
188
|
+
/** Marks the control as pristine (unmodified) */
|
|
189
|
+
markPristine: () => void;
|
|
190
|
+
/**
|
|
191
|
+
* Sets the disabled state of the control.
|
|
192
|
+
* @param disabled - True to disable, false to enable
|
|
193
|
+
*/
|
|
194
|
+
setDisabled: (disabled: boolean) => void;
|
|
195
|
+
/**
|
|
196
|
+
* Adds a manual error with key and message.
|
|
197
|
+
* @param key - Error identifier key
|
|
198
|
+
* @param message - Error message
|
|
199
|
+
*/
|
|
200
|
+
setError: (key: string, message: string) => void;
|
|
201
|
+
/**
|
|
202
|
+
* Removes a specific manual error by key.
|
|
203
|
+
* @param key - Error identifier key to remove
|
|
204
|
+
*/
|
|
205
|
+
clearError: (key: string) => void;
|
|
206
|
+
/** Removes all manual errors */
|
|
207
|
+
clearErrors: () => void;
|
|
208
|
+
/**
|
|
209
|
+
* Adds a manual warning with key and message.
|
|
210
|
+
* @param key - Warning identifier key
|
|
211
|
+
* @param message - Warning message
|
|
212
|
+
*/
|
|
213
|
+
setWarning: (key: string, message: string) => void;
|
|
214
|
+
/**
|
|
215
|
+
* Removes a specific manual warning by key.
|
|
216
|
+
* @param key - Warning identifier key to remove
|
|
217
|
+
*/
|
|
218
|
+
clearWarning: (key: string) => void;
|
|
219
|
+
/** Removes all manual warnings */
|
|
220
|
+
clearWarnings: () => void;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Represents a form array - a collection of form controls/groups with dynamic add/remove capabilities.
|
|
224
|
+
*
|
|
225
|
+
* Manages an array of controls where each item can be a control, group, or nested array.
|
|
226
|
+
* Provides methods for array manipulation (push, insert, remove) and tracks collective state.
|
|
227
|
+
*
|
|
228
|
+
* @template TValue - The type of each item in the array
|
|
229
|
+
*/
|
|
230
|
+
interface SignalFormArray<TValue> {
|
|
231
|
+
/** Discriminator property set to 'array' for type checking */
|
|
232
|
+
kind: 'array';
|
|
233
|
+
/** Signal containing the array of child controls */
|
|
234
|
+
controls: Signal<SignalFormControlLike<TValue>[]>;
|
|
235
|
+
/** Signal containing array of extracted values from all controls */
|
|
236
|
+
value: Signal<SignalFormValueFor<TValue>[]>;
|
|
237
|
+
/** Signal containing array of error structures from all controls */
|
|
238
|
+
errors: Signal<SignalFormErrorFor<TValue>[]>;
|
|
239
|
+
/** Signal containing array of warning structures from all controls */
|
|
240
|
+
warnings: Signal<SignalFormErrorFor<TValue>[]>;
|
|
241
|
+
/** Signal containing manually set errors on the array itself */
|
|
242
|
+
selfErrors: Signal<Record<string, string>>;
|
|
243
|
+
/** Signal containing manually set warnings on the array itself */
|
|
244
|
+
selfWarnings: Signal<Record<string, string>>;
|
|
245
|
+
/** Signal indicating if the array is disabled */
|
|
246
|
+
disabled: Signal<boolean>;
|
|
247
|
+
/** Signal true when array or any child is touched */
|
|
248
|
+
touched: Signal<boolean>;
|
|
249
|
+
/** Signal true when array or any child is dirty */
|
|
250
|
+
dirty: Signal<boolean>;
|
|
251
|
+
/** Signal true when array or any child has errors */
|
|
252
|
+
invalid: Signal<boolean>;
|
|
253
|
+
/** Signal true when array and all children have no errors */
|
|
254
|
+
valid: Signal<boolean>;
|
|
255
|
+
/**
|
|
256
|
+
* Inserts a control at specified index (defaults to end).
|
|
257
|
+
* @param item - Item to insert (value, config, or control)
|
|
258
|
+
* @param index - Position to insert at (optional, defaults to end)
|
|
259
|
+
* @returns Index where item was inserted
|
|
260
|
+
*/
|
|
261
|
+
insert: (item: SignalFormInput<TValue, any> | SignalFormValueFor<TValue>, index?: number) => number;
|
|
262
|
+
/**
|
|
263
|
+
* Appends a control to the end of the array.
|
|
264
|
+
* @param item - Item to append (value, config, or control)
|
|
265
|
+
* @returns Index where item was inserted
|
|
266
|
+
*/
|
|
267
|
+
push: (item: SignalFormInput<TValue, any> | SignalFormValueFor<TValue>) => number;
|
|
268
|
+
/**
|
|
269
|
+
* Removes the control at specified index.
|
|
270
|
+
* @param index - Index of control to remove
|
|
271
|
+
*/
|
|
272
|
+
removeAt: (index: number) => void;
|
|
273
|
+
/** Removes all controls from the array */
|
|
274
|
+
clear: () => void;
|
|
275
|
+
/**
|
|
276
|
+
* Retrieves the control at specified index.
|
|
277
|
+
* @param index - Index of control to retrieve
|
|
278
|
+
* @returns Control at index, or undefined if out of bounds
|
|
279
|
+
*/
|
|
280
|
+
at: (index: number) => SignalFormControlLike<TValue> | undefined;
|
|
281
|
+
/**
|
|
282
|
+
* Sets values for all controls (recreates if length differs).
|
|
283
|
+
* @param value - Array of new values
|
|
284
|
+
* @param options - Optional flags for marking dirty/touched
|
|
285
|
+
*/
|
|
286
|
+
setValue: (value: SignalFormValueFor<TValue>[], options?: SignalFormSetValueOptions) => void;
|
|
287
|
+
/**
|
|
288
|
+
* Resets all controls to initial state or provided values.
|
|
289
|
+
* @param value - Optional new initial values
|
|
290
|
+
*/
|
|
291
|
+
reset: (value?: SignalFormValueFor<TValue>[]) => void;
|
|
292
|
+
/** Marks the array itself as touched */
|
|
293
|
+
markTouched: () => void;
|
|
294
|
+
/** Marks the array itself as untouched */
|
|
295
|
+
markUntouched: () => void;
|
|
296
|
+
/** Marks the array itself as dirty */
|
|
297
|
+
markDirty: () => void;
|
|
298
|
+
/** Marks the array itself as pristine */
|
|
299
|
+
markPristine: () => void;
|
|
300
|
+
/** Marks the array and all children as touched */
|
|
301
|
+
markAllTouched: () => void;
|
|
302
|
+
/**
|
|
303
|
+
* Sets disabled state for array and optionally children.
|
|
304
|
+
* @param disabled - True to disable, false to enable
|
|
305
|
+
* @param options - Options to control if children are affected
|
|
306
|
+
*/
|
|
307
|
+
setDisabled: (disabled: boolean, options?: SignalFormDisableOptions) => void;
|
|
308
|
+
/**
|
|
309
|
+
* Adds a manual error to the array.
|
|
310
|
+
* @param key - Error identifier key
|
|
311
|
+
* @param message - Error message
|
|
312
|
+
*/
|
|
313
|
+
setError: (key: string, message: string) => void;
|
|
314
|
+
/**
|
|
315
|
+
* Removes a specific manual error.
|
|
316
|
+
* @param key - Error identifier key to remove
|
|
317
|
+
*/
|
|
318
|
+
clearError: (key: string) => void;
|
|
319
|
+
/** Removes all manual errors */
|
|
320
|
+
clearErrors: () => void;
|
|
321
|
+
/**
|
|
322
|
+
* Adds a manual warning to the array.
|
|
323
|
+
* @param key - Warning identifier key
|
|
324
|
+
* @param message - Warning message
|
|
325
|
+
*/
|
|
326
|
+
setWarning: (key: string, message: string) => void;
|
|
327
|
+
/**
|
|
328
|
+
* Removes a specific manual warning.
|
|
329
|
+
* @param key - Warning identifier key to remove
|
|
330
|
+
*/
|
|
331
|
+
clearWarning: (key: string) => void;
|
|
332
|
+
/** Removes all manual warnings */
|
|
333
|
+
clearWarnings: () => void;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Represents a form group - a structured collection of named form controls.
|
|
337
|
+
*
|
|
338
|
+
* Manages an object/record of controls where each property is a control, group, or array.
|
|
339
|
+
* Provides type-safe access to controls and tracks collective validation state.
|
|
340
|
+
*
|
|
341
|
+
* @template TData - The object type defining the structure and types of all controls
|
|
342
|
+
*/
|
|
343
|
+
interface SignalForm<TData extends object> {
|
|
344
|
+
/** Discriminator property set to 'group' for type checking */
|
|
345
|
+
kind: 'group';
|
|
346
|
+
/** Object containing all named child controls */
|
|
347
|
+
controls: {
|
|
348
|
+
[Key in keyof TData]: SignalFormControlLike<TData[Key]>;
|
|
349
|
+
};
|
|
350
|
+
/** Signal containing object with extracted values from all controls */
|
|
351
|
+
value: Signal<{
|
|
352
|
+
[Key in keyof TData]: SignalFormValueFor<TData[Key]>;
|
|
353
|
+
}>;
|
|
354
|
+
/** Signal containing object with error structures from all controls */
|
|
355
|
+
errors: Signal<{
|
|
356
|
+
[Key in keyof TData]: SignalFormErrorFor<TData[Key]>;
|
|
357
|
+
}>;
|
|
358
|
+
/** Signal containing object with warning structures from all controls */
|
|
359
|
+
warnings: Signal<{
|
|
360
|
+
[Key in keyof TData]: SignalFormErrorFor<TData[Key]>;
|
|
361
|
+
}>;
|
|
362
|
+
/** Signal containing manually set errors on the group itself */
|
|
363
|
+
selfErrors: Signal<Record<string, string>>;
|
|
364
|
+
/** Signal containing manually set warnings on the group itself */
|
|
365
|
+
selfWarnings: Signal<Record<string, string>>;
|
|
366
|
+
/** Signal indicating if the group is disabled */
|
|
367
|
+
disabled: Signal<boolean>;
|
|
368
|
+
/** Signal true when group or any child is touched */
|
|
369
|
+
touched: Signal<boolean>;
|
|
370
|
+
/** Signal true when group or any child is dirty */
|
|
371
|
+
dirty: Signal<boolean>;
|
|
372
|
+
/** Signal true when group or any child has errors */
|
|
373
|
+
invalid: Signal<boolean>;
|
|
374
|
+
/** Signal true when group and all children have no errors */
|
|
375
|
+
valid: Signal<boolean>;
|
|
376
|
+
/**
|
|
377
|
+
* Type-safe method to retrieve a specific control by key.
|
|
378
|
+
* @param key - Control property name
|
|
379
|
+
* @returns The control for the specified key
|
|
380
|
+
*/
|
|
381
|
+
getControl: <Key extends keyof TData>(key: Key) => SignalFormControlLike<TData[Key]>;
|
|
382
|
+
/**
|
|
383
|
+
* Updates values for specified controls (partial updates supported).
|
|
384
|
+
* @param value - Partial object with new values
|
|
385
|
+
* @param options - Optional flags for marking dirty/touched
|
|
386
|
+
*/
|
|
387
|
+
setValue: (value: Partial<{
|
|
388
|
+
[Key in keyof TData]: SignalFormValueFor<TData[Key]>;
|
|
389
|
+
}>, options?: SignalFormSetValueOptions) => void;
|
|
390
|
+
/**
|
|
391
|
+
* Resets all controls to initial state or provided values.
|
|
392
|
+
* @param value - Optional partial object with new initial values
|
|
393
|
+
*/
|
|
394
|
+
reset: (value?: Partial<{
|
|
395
|
+
[Key in keyof TData]: SignalFormValueFor<TData[Key]>;
|
|
396
|
+
}>) => void;
|
|
397
|
+
/** Marks the group itself as touched */
|
|
398
|
+
markTouched: () => void;
|
|
399
|
+
/** Marks the group itself as untouched */
|
|
400
|
+
markUntouched: () => void;
|
|
401
|
+
/** Marks the group itself as dirty */
|
|
402
|
+
markDirty: () => void;
|
|
403
|
+
/** Marks the group itself as pristine */
|
|
404
|
+
markPristine: () => void;
|
|
405
|
+
/** Marks the group and all children as touched */
|
|
406
|
+
markAllTouched: () => void;
|
|
407
|
+
/**
|
|
408
|
+
* Sets disabled state for group and optionally children.
|
|
409
|
+
* @param disabled - True to disable, false to enable
|
|
410
|
+
* @param options - Options to control if children are affected
|
|
411
|
+
*/
|
|
412
|
+
setDisabled: (disabled: boolean, options?: SignalFormDisableOptions) => void;
|
|
413
|
+
/**
|
|
414
|
+
* Adds a manual error to the group.
|
|
415
|
+
* @param key - Error identifier key
|
|
416
|
+
* @param message - Error message
|
|
417
|
+
*/
|
|
418
|
+
setError: (key: string, message: string) => void;
|
|
419
|
+
/**
|
|
420
|
+
* Removes a specific manual error.
|
|
421
|
+
* @param key - Error identifier key to remove
|
|
422
|
+
*/
|
|
423
|
+
clearError: (key: string) => void;
|
|
424
|
+
/** Removes all manual errors */
|
|
425
|
+
clearErrors: () => void;
|
|
426
|
+
/**
|
|
427
|
+
* Adds a manual warning to the group.
|
|
428
|
+
* @param key - Warning identifier key
|
|
429
|
+
* @param message - Warning message
|
|
430
|
+
*/
|
|
431
|
+
setWarning: (key: string, message: string) => void;
|
|
432
|
+
/**
|
|
433
|
+
* Removes a specific manual warning.
|
|
434
|
+
* @param key - Warning identifier key to remove
|
|
435
|
+
*/
|
|
436
|
+
clearWarning: (key: string) => void;
|
|
437
|
+
/** Removes all manual warnings */
|
|
438
|
+
clearWarnings: () => void;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Type utility that maps a value type to its appropriate control interface.
|
|
442
|
+
*
|
|
443
|
+
* Automatically determines the correct control type based on the value:
|
|
444
|
+
* - Arrays → SignalFormArray
|
|
445
|
+
* - Objects → SignalForm (group)
|
|
446
|
+
* - Primitives → SignalFormControl
|
|
447
|
+
*
|
|
448
|
+
* @template TValue - The value type to map to a control interface
|
|
449
|
+
*
|
|
450
|
+
* @example
|
|
451
|
+
* ```typescript
|
|
452
|
+
* type StringControl = SignalFormControlLike<string>; // SignalFormControl<string>
|
|
453
|
+
* type PersonControl = SignalFormControlLike<Person>; // SignalForm<Person>
|
|
454
|
+
* type HobbiesControl = SignalFormControlLike<string[]>; // SignalFormArray<string>
|
|
455
|
+
* ```
|
|
456
|
+
*/
|
|
457
|
+
type SignalFormControlLike<TValue> = TValue extends (infer U)[] ? SignalFormArray<U> : TValue extends object ? SignalForm<TValue> : SignalFormControl<TValue>;
|
|
458
|
+
/**
|
|
459
|
+
* Context object passed to validators and disabled functions.
|
|
460
|
+
*
|
|
461
|
+
* Provides access to the current control's value and sibling controls,
|
|
462
|
+
* enabling cross-field validation and dynamic behavior based on other form values.
|
|
463
|
+
*
|
|
464
|
+
* @template TControls - Object type defining all available sibling controls
|
|
465
|
+
* @template TValue - The type of the current control's value
|
|
466
|
+
*
|
|
467
|
+
* @example
|
|
468
|
+
* ```typescript
|
|
469
|
+
* const validator: SignalFormValidatorFn<FormModel, string> = (ctx) => {
|
|
470
|
+
* const otherControl = ctx.getControl('otherField');
|
|
471
|
+
* return ctx.item.value === otherControl.value() ? null : { mismatch: 'Values must match' };
|
|
472
|
+
* };
|
|
473
|
+
* ```
|
|
474
|
+
*/
|
|
475
|
+
interface SignalFormContext<TControls extends object, TValue> {
|
|
476
|
+
/** Object containing the current control's value */
|
|
477
|
+
item: {
|
|
478
|
+
value: TValue | undefined;
|
|
479
|
+
};
|
|
480
|
+
/**
|
|
481
|
+
* Function to retrieve sibling controls by key for cross-field logic.
|
|
482
|
+
* @param controlName - Key of the sibling control to retrieve
|
|
483
|
+
* @returns The sibling control
|
|
484
|
+
*/
|
|
485
|
+
getControl: <ControlKey extends keyof TControls>(controlName: ControlKey) => SignalFormControlLike<TControls[ControlKey]>;
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Return type for validation functions.
|
|
489
|
+
*
|
|
490
|
+
* Validators return a map of error keys to messages when validation fails,
|
|
491
|
+
* or null/empty when validation passes. Empty strings and null values are ignored.
|
|
492
|
+
*
|
|
493
|
+
* @example
|
|
494
|
+
* ```typescript
|
|
495
|
+
* const result: SignalFormValidationError = { required: 'Field is required', min: 'Too small' };
|
|
496
|
+
* const success: SignalFormValidationError = null;
|
|
497
|
+
* ```
|
|
498
|
+
*/
|
|
499
|
+
type SignalFormValidationError = Record<string, string | undefined | null> | null;
|
|
500
|
+
/**
|
|
501
|
+
* Function signature for validators and warnings.
|
|
502
|
+
*
|
|
503
|
+
* Takes a context with the current value and sibling controls,
|
|
504
|
+
* returns error/warning messages or null when validation passes.
|
|
505
|
+
*
|
|
506
|
+
* @template TControls - Object type defining available sibling controls
|
|
507
|
+
* @template TValue - The type of value being validated
|
|
508
|
+
*
|
|
509
|
+
* @param ctx - Validation context with current value and control accessor
|
|
510
|
+
* @returns Error map when validation fails, null when it passes
|
|
511
|
+
*
|
|
512
|
+
* @example
|
|
513
|
+
* ```typescript
|
|
514
|
+
* const minValidator: SignalFormValidatorFn<any, number> = ({ item }) => {
|
|
515
|
+
* return item.value && item.value < 0 ? { min: 'Must be positive' } : null;
|
|
516
|
+
* };
|
|
517
|
+
* ```
|
|
518
|
+
*/
|
|
519
|
+
type SignalFormValidatorFn<TControls extends object, TValue> = (ctx: SignalFormContext<TControls, TValue>) => SignalFormValidationError;
|
|
520
|
+
/**
|
|
521
|
+
* Configuration object for creating a form control with advanced options.
|
|
522
|
+
*
|
|
523
|
+
* Allows specifying initial value, validators, warnings, and dynamic disabled logic.
|
|
524
|
+
*
|
|
525
|
+
* @template TValue - The type of value the control will manage
|
|
526
|
+
* @template TControls - Object type defining available sibling controls for validators
|
|
527
|
+
*
|
|
528
|
+
* @example
|
|
529
|
+
* ```typescript
|
|
530
|
+
* const config: SignalFormControlConfig<number, FormModel> = {
|
|
531
|
+
* value: 0,
|
|
532
|
+
* validators: [signalFormValidators.min(0)],
|
|
533
|
+
* warnings: [signalFormValidators.max(100)],
|
|
534
|
+
* disabled: (ctx) => ctx.getControl('otherField').value() === 'locked'
|
|
535
|
+
* };
|
|
536
|
+
* ```
|
|
537
|
+
*/
|
|
538
|
+
interface SignalFormControlConfig<TValue, TControls extends object> {
|
|
539
|
+
/** Initial value (can be a signal or static value) */
|
|
540
|
+
value: SignalOrValue<TValue | undefined>;
|
|
541
|
+
/** Disabled state (boolean, signal, or function based on form context) */
|
|
542
|
+
disabled?: SignalOrValue<boolean> | SignalFormDisabledFn<TControls, TValue>;
|
|
543
|
+
/** Array of validation functions that mark control as invalid */
|
|
544
|
+
validators?: SignalFormValidatorFn<TControls, TValue>[];
|
|
545
|
+
/** Array of validation functions that don't affect validity */
|
|
546
|
+
warnings?: SignalFormValidatorFn<TControls, TValue>[];
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Input type accepted when creating a form control.
|
|
550
|
+
*
|
|
551
|
+
* Flexible input that accepts:
|
|
552
|
+
* - A raw value (primitive, signal)
|
|
553
|
+
* - A configuration object with validators and options
|
|
554
|
+
* - An existing SignalFormControl instance
|
|
555
|
+
*
|
|
556
|
+
* @template TValue - The value type for the control
|
|
557
|
+
* @template TControls - Object type defining available sibling controls
|
|
558
|
+
*/
|
|
559
|
+
type SignalFormControlInput<TValue, TControls extends object> = SignalOrValue<TValue | undefined> | SignalFormControlConfig<TValue, TControls> | SignalFormControl<TValue>;
|
|
560
|
+
/**
|
|
561
|
+
* Input type accepted when creating a form group.
|
|
562
|
+
*
|
|
563
|
+
* @template TData - Object type defining the structure of the group
|
|
564
|
+
*/
|
|
565
|
+
type SignalFormGroupInput<TData extends object> = SignalFormInputs<TData> | SignalForm<TData>;
|
|
566
|
+
/**
|
|
567
|
+
* Input type accepted when creating a form array.
|
|
568
|
+
*
|
|
569
|
+
* @template TItem - The type of each item in the array
|
|
570
|
+
* @template TControls - Object type defining available sibling controls
|
|
571
|
+
*/
|
|
572
|
+
type SignalFormArrayInput<TItem, TControls extends object> = SignalFormArray<TItem> | SignalFormInput<TItem, TControls>[];
|
|
573
|
+
/**
|
|
574
|
+
* Recursive input type that automatically maps to the correct control input type.
|
|
575
|
+
*
|
|
576
|
+
* Determines the appropriate input type based on value structure:
|
|
577
|
+
* - Arrays → SignalFormArrayInput
|
|
578
|
+
* - Objects → SignalFormGroupInput
|
|
579
|
+
* - Primitives → SignalFormControlInput
|
|
580
|
+
*
|
|
581
|
+
* @template TValue - The value type to create an input for
|
|
582
|
+
* @template TControls - Object type defining available sibling controls
|
|
583
|
+
*/
|
|
584
|
+
type SignalFormInput<TValue, TControls extends object> = TValue extends (infer U)[] ? SignalFormArrayInput<U, TControls> : TValue extends object ? SignalFormGroupInput<TValue> : SignalFormControlInput<TValue, TControls>;
|
|
585
|
+
/**
|
|
586
|
+
* Type for defining the inputs of a form group.
|
|
587
|
+
*
|
|
588
|
+
* Maps each property of TData to its appropriate input type,
|
|
589
|
+
* all properties are optional to allow partial form definitions.
|
|
590
|
+
*
|
|
591
|
+
* @template TData - Object type defining the structure of the form
|
|
592
|
+
*/
|
|
593
|
+
type SignalFormInputs<TData extends object> = {
|
|
594
|
+
[Key in keyof TData]?: SignalFormInput<TData[Key], TData>;
|
|
595
|
+
};
|
|
596
|
+
/**
|
|
597
|
+
* Function signature for dynamic disabled logic.
|
|
598
|
+
*
|
|
599
|
+
* Determines if a control should be disabled based on form context.
|
|
600
|
+
*
|
|
601
|
+
* @template TControls - Object type defining available sibling controls
|
|
602
|
+
* @template TValue - The type of value in the control
|
|
603
|
+
*
|
|
604
|
+
* @param ctx - Context with current value and sibling controls
|
|
605
|
+
* @returns Boolean indicating if control should be disabled
|
|
606
|
+
*/
|
|
607
|
+
type SignalFormDisabledFn<TControls extends object, TValue> = (ctx: SignalFormContext<TControls, TValue>) => boolean;
|
|
608
|
+
/**
|
|
609
|
+
* Options for setValue operations.
|
|
610
|
+
*/
|
|
611
|
+
interface SignalFormSetValueOptions {
|
|
612
|
+
/** Whether to mark the control as dirty (default: true) */
|
|
613
|
+
markDirty?: boolean;
|
|
614
|
+
/** Whether to mark the control as touched (default: false) */
|
|
615
|
+
markTouched?: boolean;
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Options for setDisabled operations.
|
|
619
|
+
*/
|
|
620
|
+
interface SignalFormDisableOptions {
|
|
621
|
+
/** If true, only disable this control without affecting children (default: false) */
|
|
622
|
+
onlySelf?: boolean;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Internal type for accessing sibling controls in a form.
|
|
627
|
+
*
|
|
628
|
+
* @internal
|
|
629
|
+
*/
|
|
630
|
+
type ControlAccessor$1<TControls extends object> = <Key extends keyof TControls>(controlName: Key) => SignalFormControlLike<TControls[Key]>;
|
|
631
|
+
/**
|
|
632
|
+
* Type guard to check if an object is a SignalFormArray.
|
|
633
|
+
*
|
|
634
|
+
* @template TValue - The type of items in the array
|
|
635
|
+
* @param obj - Object to check
|
|
636
|
+
* @returns True if obj is a SignalFormArray
|
|
637
|
+
*
|
|
638
|
+
* @example
|
|
639
|
+
* ```typescript
|
|
640
|
+
* if (isSignalFormArray(value)) {
|
|
641
|
+
* console.log(value.controls().length); // TypeScript knows this is an array
|
|
642
|
+
* }
|
|
643
|
+
* ```
|
|
644
|
+
*/
|
|
645
|
+
declare function isSignalFormArray<TValue>(obj: unknown): obj is SignalFormArray<TValue>;
|
|
646
|
+
/**
|
|
647
|
+
* Creates a reactive form array for managing dynamic collections of controls.
|
|
648
|
+
*
|
|
649
|
+
* Builds an array container that can hold multiple controls (primitives, groups, or nested arrays)
|
|
650
|
+
* with full signal-based reactivity. Provides methods for dynamic addition/removal of items
|
|
651
|
+
* and tracks collective validation state.
|
|
652
|
+
*
|
|
653
|
+
* Features:
|
|
654
|
+
* - Dynamic array operations (push, insert, remove, clear)
|
|
655
|
+
* - Reactive value and error tracking across all items
|
|
656
|
+
* - Collective state management (touched, dirty, valid)
|
|
657
|
+
* - Manual error/warning management at array level
|
|
658
|
+
* - Type-safe access to individual controls
|
|
659
|
+
*
|
|
660
|
+
* @template TItem - The type of each item in the array
|
|
661
|
+
* @template TControls - Object type defining available sibling controls
|
|
662
|
+
*
|
|
663
|
+
* @param inputItems - Array of initial items (values, configs, or controls)
|
|
664
|
+
* @param getControl - Optional accessor for sibling controls
|
|
665
|
+
* @returns Fully configured SignalFormArray instance
|
|
666
|
+
*
|
|
667
|
+
* @example
|
|
668
|
+
* ```typescript
|
|
669
|
+
* // Array of primitives
|
|
670
|
+
* const tagsArray = createSignalFormArray(['tag1', 'tag2']);
|
|
671
|
+
* tagsArray.push('tag3');
|
|
672
|
+
*
|
|
673
|
+
* // Array of objects
|
|
674
|
+
* const addressesArray = createSignalFormArray<Address>([
|
|
675
|
+
* { street: '123 Main', city: 'NYC' },
|
|
676
|
+
* { street: '456 Oak', city: 'LA' }
|
|
677
|
+
* ]);
|
|
678
|
+
*
|
|
679
|
+
* // Array with validators
|
|
680
|
+
* const hobbiesArray = createSignalFormArray([
|
|
681
|
+
* { value: 'coding', validators: [signalFormValidators.minLength(3)] },
|
|
682
|
+
* 'gaming'
|
|
683
|
+
* ]);
|
|
684
|
+
* ```
|
|
685
|
+
*/
|
|
686
|
+
declare function createSignalFormArray<TItem, TControls extends object = object>(inputItems: SignalFormInput<TItem, TControls>[], getControl?: ControlAccessor$1<TControls>): SignalFormArray<TItem>;
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* Internal type for accessing sibling controls in a form.
|
|
690
|
+
*
|
|
691
|
+
* @internal
|
|
692
|
+
*/
|
|
693
|
+
type ControlAccessor<TControls extends object> = <Key extends keyof TControls>(controlName: Key) => SignalFormControlLike<TControls[Key]>;
|
|
694
|
+
/**
|
|
695
|
+
* Type guard to check if an object is a SignalFormControl.
|
|
696
|
+
*
|
|
697
|
+
* @template TValue - The type of value the control manages
|
|
698
|
+
* @param obj - Object to check
|
|
699
|
+
* @returns True if obj is a SignalFormControl
|
|
700
|
+
*
|
|
701
|
+
* @example
|
|
702
|
+
* ```typescript
|
|
703
|
+
* if (isSignalFormControl(value)) {
|
|
704
|
+
* console.log(value.value()); // TypeScript knows this is a control
|
|
705
|
+
* }
|
|
706
|
+
* ```
|
|
707
|
+
*/
|
|
708
|
+
declare function isSignalFormControl<TValue>(obj: unknown): obj is SignalFormControl<TValue>;
|
|
709
|
+
/**
|
|
710
|
+
* Creates a reactive form control with signal-based state management.
|
|
711
|
+
*
|
|
712
|
+
* Builds a control that wraps a primitive value (string, number, boolean, etc.)
|
|
713
|
+
* with validation, state tracking, and reactive updates using Angular signals.
|
|
714
|
+
*
|
|
715
|
+
* Features:
|
|
716
|
+
* - Reactive value updates through signals
|
|
717
|
+
* - Validators for errors (mark control as invalid)
|
|
718
|
+
* - Warnings (validation messages without invalidating)
|
|
719
|
+
* - Dynamic disabled state based on form context
|
|
720
|
+
* - State tracking (touched, dirty)
|
|
721
|
+
* - Manual error/warning management
|
|
722
|
+
*
|
|
723
|
+
* @template TControls - Object type defining available sibling controls for cross-field validation
|
|
724
|
+
* @template TValue - The type of value this control manages
|
|
725
|
+
*
|
|
726
|
+
* @param input - Control input (raw value, config object, or existing control)
|
|
727
|
+
* @param getControl - Optional accessor function for sibling controls
|
|
728
|
+
* @returns Fully configured SignalFormControl instance
|
|
729
|
+
*
|
|
730
|
+
* @example
|
|
731
|
+
* ```typescript
|
|
732
|
+
* // Simple control
|
|
733
|
+
* const nameControl = createSignalFormControl('John');
|
|
734
|
+
*
|
|
735
|
+
* // With validators
|
|
736
|
+
* const ageControl = createSignalFormControl({
|
|
737
|
+
* value: 25,
|
|
738
|
+
* validators: [signalFormValidators.required, signalFormValidators.min(0)],
|
|
739
|
+
* warnings: [signalFormValidators.max(120)]
|
|
740
|
+
* });
|
|
741
|
+
*
|
|
742
|
+
* // With dynamic disabled
|
|
743
|
+
* const emailControl = createSignalFormControl({
|
|
744
|
+
* value: '',
|
|
745
|
+
* disabled: (ctx) => ctx.getControl('accountType').value() === 'guest'
|
|
746
|
+
* });
|
|
747
|
+
* ```
|
|
748
|
+
*/
|
|
749
|
+
declare function createSignalFormControl<TControls extends object, TValue>(input: SignalFormControlInput<TValue, TControls>, getControl?: ControlAccessor<TControls>): SignalFormControl<TValue>;
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* Type guard to check if an object is a SignalForm (form group).
|
|
753
|
+
*
|
|
754
|
+
* @template TData - Object type defining the form structure
|
|
755
|
+
* @param obj - Object to check
|
|
756
|
+
* @returns True if obj is a SignalForm
|
|
757
|
+
*
|
|
758
|
+
* @example
|
|
759
|
+
* ```typescript
|
|
760
|
+
* if (isSignalFormGroup(value)) {
|
|
761
|
+
* console.log(value.controls.name); // TypeScript knows this is a form group
|
|
762
|
+
* }
|
|
763
|
+
* ```
|
|
764
|
+
*/
|
|
765
|
+
declare function isSignalFormGroup<TData extends object>(obj: unknown): obj is SignalForm<TData>;
|
|
766
|
+
/**
|
|
767
|
+
* Creates a reactive form group for managing structured form data.
|
|
768
|
+
*
|
|
769
|
+
* Builds a typed form container that holds multiple named controls, groups, or arrays.
|
|
770
|
+
* Each property in the input object becomes a control with full signal-based reactivity.
|
|
771
|
+
* Provides type-safe access to controls and tracks collective validation state.
|
|
772
|
+
*
|
|
773
|
+
* Features:
|
|
774
|
+
* - Type-safe control access via `.controls` property
|
|
775
|
+
* - Reactive value and error tracking across all controls
|
|
776
|
+
* - Collective state management (touched, dirty, valid)
|
|
777
|
+
* - Manual error/warning management at group level
|
|
778
|
+
* - Support for nested groups and arrays
|
|
779
|
+
* - Cross-field validation via getControl accessor
|
|
780
|
+
*
|
|
781
|
+
* @template TData - Object type defining the structure and types of all controls
|
|
782
|
+
*
|
|
783
|
+
* @param inputs - Object mapping property names to their control inputs
|
|
784
|
+
* @returns Fully configured SignalForm instance
|
|
785
|
+
*
|
|
786
|
+
* @example
|
|
787
|
+
* ```typescript
|
|
788
|
+
* // Simple form
|
|
789
|
+
* const form = createSignalFormGroup({
|
|
790
|
+
* name: 'John',
|
|
791
|
+
* age: 25
|
|
792
|
+
* });
|
|
793
|
+
*
|
|
794
|
+
* // With validators and nested structure
|
|
795
|
+
* const form = createSignalFormGroup<User>({
|
|
796
|
+
* email: {
|
|
797
|
+
* value: '',
|
|
798
|
+
* validators: [signalFormValidators.required, signalFormValidators.email]
|
|
799
|
+
* },
|
|
800
|
+
* age: {
|
|
801
|
+
* value: 25,
|
|
802
|
+
* validators: [signalFormValidators.min(0)],
|
|
803
|
+
* warnings: [signalFormValidators.max(120)]
|
|
804
|
+
* },
|
|
805
|
+
* address: {
|
|
806
|
+
* street: '123 Main St',
|
|
807
|
+
* city: 'NYC'
|
|
808
|
+
* },
|
|
809
|
+
* hobbies: ['coding', 'gaming']
|
|
810
|
+
* });
|
|
811
|
+
*
|
|
812
|
+
* // Access controls
|
|
813
|
+
* form.controls.email.value(); // Type-safe access
|
|
814
|
+
* form.getControl('age').setValue(30);
|
|
815
|
+
* ```
|
|
816
|
+
*/
|
|
817
|
+
declare function createSignalFormGroup<TData extends object>(inputs: SignalFormInputs<TData>): SignalForm<TData>;
|
|
818
|
+
/**
|
|
819
|
+
* Helper function to create a standalone form control.
|
|
820
|
+
*
|
|
821
|
+
* Creates a control without sibling control access. Useful for creating
|
|
822
|
+
* individual controls outside of a form group context.
|
|
823
|
+
*
|
|
824
|
+
* @template TValue - The type of value the control manages
|
|
825
|
+
* @param input - Control input (raw value, config object, or existing control)
|
|
826
|
+
* @returns SignalFormControl instance
|
|
827
|
+
*
|
|
828
|
+
* @example
|
|
829
|
+
* ```typescript
|
|
830
|
+
* const nameControl = formControl('John');
|
|
831
|
+
* const ageControl = formControl({
|
|
832
|
+
* value: 25,
|
|
833
|
+
* validators: [signalFormValidators.min(0)]
|
|
834
|
+
* });
|
|
835
|
+
* ```
|
|
836
|
+
*/
|
|
837
|
+
declare function formControl<TValue>(input: SignalFormControlInput<TValue, object>): SignalFormControl<TValue>;
|
|
838
|
+
/**
|
|
839
|
+
* Helper function to create a standalone form array.
|
|
840
|
+
*
|
|
841
|
+
* Creates an array without sibling control access. Useful for creating
|
|
842
|
+
* array controls outside of a form group context.
|
|
843
|
+
*
|
|
844
|
+
* @template TValue - The type of each item in the array
|
|
845
|
+
* @param input - Array of initial items
|
|
846
|
+
* @returns SignalFormArray instance
|
|
847
|
+
*
|
|
848
|
+
* @example
|
|
849
|
+
* ```typescript
|
|
850
|
+
* const tagsArray = formArray(['tag1', 'tag2', 'tag3']);
|
|
851
|
+
* const addressesArray = formArray<Address>([
|
|
852
|
+
* { street: '123 Main', city: 'NYC' }
|
|
853
|
+
* ]);
|
|
854
|
+
* ```
|
|
855
|
+
*/
|
|
856
|
+
declare function formArray<TValue>(input: SignalFormInput<TValue, object>[]): SignalFormArray<TValue>;
|
|
857
|
+
/**
|
|
858
|
+
* Helper function to create a form group.
|
|
859
|
+
*
|
|
860
|
+
* Alias for createSignalFormGroup. Creates a typed form with multiple controls.
|
|
861
|
+
*
|
|
862
|
+
* @template TData - Object type defining the form structure
|
|
863
|
+
* @param input - Object mapping property names to control inputs
|
|
864
|
+
* @returns SignalForm instance
|
|
865
|
+
*
|
|
866
|
+
* @example
|
|
867
|
+
* ```typescript
|
|
868
|
+
* const form = formGroup({
|
|
869
|
+
* name: 'John',
|
|
870
|
+
* email: {
|
|
871
|
+
* value: 'john@example.com',
|
|
872
|
+
* validators: [signalFormValidators.email]
|
|
873
|
+
* }
|
|
874
|
+
* });
|
|
875
|
+
* ```
|
|
876
|
+
*/
|
|
877
|
+
declare function formGroup<TData extends object>(input: SignalFormInputs<TData>): SignalForm<TData>;
|
|
878
|
+
/**
|
|
879
|
+
* Primary API for creating signal-based reactive forms.
|
|
880
|
+
*
|
|
881
|
+
* Alias for `formGroup`. This is the main entry point for creating forms.
|
|
882
|
+
* Provides type-safe, signal-based form state management with built-in validation.
|
|
883
|
+
*
|
|
884
|
+
* @example
|
|
885
|
+
* ```typescript
|
|
886
|
+
* // Basic form
|
|
887
|
+
* const form = signalForm({ name: 'John', age: 25 });
|
|
888
|
+
*
|
|
889
|
+
* // Complex form with validation
|
|
890
|
+
* const form = signalForm<Person>({
|
|
891
|
+
* name: {
|
|
892
|
+
* value: '',
|
|
893
|
+
* validators: [signalFormValidators.required, signalFormValidators.minLength(2)]
|
|
894
|
+
* },
|
|
895
|
+
* age: {
|
|
896
|
+
* value: 30,
|
|
897
|
+
* validators: [signalFormValidators.min(0)],
|
|
898
|
+
* warnings: [signalFormValidators.max(120)],
|
|
899
|
+
* disabled: (ctx) => ctx.getControl('name').value() === 'admin'
|
|
900
|
+
* },
|
|
901
|
+
* address: {
|
|
902
|
+
* street: '123 Main St',
|
|
903
|
+
* city: 'NYC'
|
|
904
|
+
* },
|
|
905
|
+
* hobbies: ['coding', 'gaming']
|
|
906
|
+
* });
|
|
907
|
+
*
|
|
908
|
+
* // Access form state
|
|
909
|
+
* console.log(form.value()); // { name: '', age: 30, address: {...}, hobbies: [...] }
|
|
910
|
+
* console.log(form.valid()); // boolean
|
|
911
|
+
* console.log(form.controls.name.errors()); // { required: 'This field is required' }
|
|
912
|
+
* ```
|
|
913
|
+
*/
|
|
914
|
+
declare const signalForm: typeof formGroup;
|
|
915
|
+
|
|
916
|
+
type ValidatorFn = SignalFormValidatorFn<any, any>;
|
|
917
|
+
/**
|
|
918
|
+
* Validator that enforces a maximum string or number length.
|
|
919
|
+
*
|
|
920
|
+
* Converts the value to string and checks if its length exceeds the specified maximum.
|
|
921
|
+
* Works with both string and number types.
|
|
922
|
+
*
|
|
923
|
+
* @param num - Maximum allowed length (inclusive)
|
|
924
|
+
* @returns Validator function
|
|
925
|
+
*
|
|
926
|
+
* @example
|
|
927
|
+
* ```typescript
|
|
928
|
+
* const control = signalForm({
|
|
929
|
+
* username: { value: 'verylongusername', validators: [signalFormValidators.maxLength(10)] }
|
|
930
|
+
* });
|
|
931
|
+
* // control.controls.username.errors() => { maxLength: 'To long' }
|
|
932
|
+
* ```
|
|
933
|
+
*/
|
|
934
|
+
declare function maxLength(num: number): ValidatorFn;
|
|
935
|
+
/**
|
|
936
|
+
* Validator that enforces a minimum string or number length.
|
|
937
|
+
*
|
|
938
|
+
* Converts the value to string and checks if its length is less than or equal to the specified minimum.
|
|
939
|
+
* Works with both string and number types.
|
|
940
|
+
*
|
|
941
|
+
* @param num - Minimum required length (inclusive)
|
|
942
|
+
* @returns Validator function
|
|
943
|
+
*
|
|
944
|
+
* @example
|
|
945
|
+
* ```typescript
|
|
946
|
+
* const control = signalForm({
|
|
947
|
+
* code: { value: 'ab', validators: [signalFormValidators.minLength(3)] }
|
|
948
|
+
* });
|
|
949
|
+
* // control.controls.code.errors() => { minLength: 'To short' }
|
|
950
|
+
* ```
|
|
951
|
+
*/
|
|
952
|
+
declare function minLength(num: number): ValidatorFn;
|
|
953
|
+
/**
|
|
954
|
+
* Validator that enforces a minimum numeric value.
|
|
955
|
+
*
|
|
956
|
+
* Checks if a numeric value is less than or equal to the specified minimum.
|
|
957
|
+
* Value is coerced to a number for comparison.
|
|
958
|
+
*
|
|
959
|
+
* @param num - Minimum allowed value (exclusive - value must be greater than this)
|
|
960
|
+
* @returns Validator function
|
|
961
|
+
*
|
|
962
|
+
* @example
|
|
963
|
+
* ```typescript
|
|
964
|
+
* const control = signalForm({
|
|
965
|
+
* age: { value: -5, validators: [signalFormValidators.min(0)] }
|
|
966
|
+
* });
|
|
967
|
+
* // control.controls.age.errors() => { minLength: 'To small' }
|
|
968
|
+
* ```
|
|
969
|
+
*/
|
|
970
|
+
declare function min(num: number): ValidatorFn;
|
|
971
|
+
/**
|
|
972
|
+
* Validator that enforces a maximum numeric value.
|
|
973
|
+
*
|
|
974
|
+
* Checks if a numeric value exceeds the specified maximum.
|
|
975
|
+
* Value is coerced to a number for comparison.
|
|
976
|
+
*
|
|
977
|
+
* @param num - Maximum allowed value (inclusive)
|
|
978
|
+
* @returns Validator function
|
|
979
|
+
*
|
|
980
|
+
* @example
|
|
981
|
+
* ```typescript
|
|
982
|
+
* const control = signalForm({
|
|
983
|
+
* age: { value: 150, validators: [signalFormValidators.max(120)] }
|
|
984
|
+
* });
|
|
985
|
+
* // control.controls.age.errors() => { minLength: 'To big' }
|
|
986
|
+
* ```
|
|
987
|
+
*/
|
|
988
|
+
declare function max(num: number): ValidatorFn;
|
|
989
|
+
/**
|
|
990
|
+
* Validator that checks if a value matches a specified regular expression pattern.
|
|
991
|
+
*
|
|
992
|
+
* Accepts either a RegExp object or a string pattern. String patterns are automatically
|
|
993
|
+
* wrapped with ^ and $ anchors to match the entire value.
|
|
994
|
+
*
|
|
995
|
+
* Skips validation for empty values (use with `required` if needed).
|
|
996
|
+
*
|
|
997
|
+
* @param valuePattern - Regular expression or pattern string to match against
|
|
998
|
+
* @returns Validator function
|
|
999
|
+
*
|
|
1000
|
+
* @example
|
|
1001
|
+
* ```typescript
|
|
1002
|
+
* // Using regex
|
|
1003
|
+
* const control1 = signalForm({
|
|
1004
|
+
* code: { value: 'abc', validators: [signalFormValidators.pattern(/^[0-9]+$/)] }
|
|
1005
|
+
* });
|
|
1006
|
+
* // control1.controls.code.errors() => { pattern: 'RequiredPattern: ^[0-9]+$, ActualValue: abc' }
|
|
1007
|
+
*
|
|
1008
|
+
* // Using string pattern
|
|
1009
|
+
* const control2 = signalForm({
|
|
1010
|
+
* zipCode: { value: 'ABC', validators: [signalFormValidators.pattern('[0-9]{5}')] }
|
|
1011
|
+
* });
|
|
1012
|
+
* ```
|
|
1013
|
+
*/
|
|
1014
|
+
declare function pattern(valuePattern: string | RegExp): ValidatorFn;
|
|
1015
|
+
/**
|
|
1016
|
+
* Collection of built-in validators for signal-form controls.
|
|
1017
|
+
*
|
|
1018
|
+
* Provides common validation functions that can be used in the `validators` or `warnings`
|
|
1019
|
+
* arrays of form controls. All validators skip empty values except `required`.
|
|
1020
|
+
*
|
|
1021
|
+
* @property required - Ensures the value is not empty (null, undefined, '', [], 0, empty Set)
|
|
1022
|
+
* @property maxLength - Ensures string/number length doesn't exceed maximum
|
|
1023
|
+
* @property minLength - Ensures string/number length meets minimum requirement
|
|
1024
|
+
* @property min - Ensures numeric value is greater than minimum (exclusive)
|
|
1025
|
+
* @property max - Ensures numeric value doesn't exceed maximum (inclusive)
|
|
1026
|
+
* @property email - Validates email address format using Angular-compatible regex
|
|
1027
|
+
* @property pattern - Validates value matches a regular expression pattern
|
|
1028
|
+
*
|
|
1029
|
+
* @example
|
|
1030
|
+
* ```typescript
|
|
1031
|
+
* const form = signalForm({
|
|
1032
|
+
* email: {
|
|
1033
|
+
* value: '',
|
|
1034
|
+
* validators: [signalFormValidators.required, signalFormValidators.email]
|
|
1035
|
+
* },
|
|
1036
|
+
* age: {
|
|
1037
|
+
* value: 25,
|
|
1038
|
+
* validators: [signalFormValidators.min(0), signalFormValidators.max(120)],
|
|
1039
|
+
* warnings: [signalFormValidators.max(100)] // Warning but doesn't invalidate
|
|
1040
|
+
* },
|
|
1041
|
+
* username: {
|
|
1042
|
+
* value: '',
|
|
1043
|
+
* validators: [
|
|
1044
|
+
* signalFormValidators.required,
|
|
1045
|
+
* signalFormValidators.minLength(3),
|
|
1046
|
+
* signalFormValidators.maxLength(20),
|
|
1047
|
+
* signalFormValidators.pattern(/^[a-zA-Z0-9_]+$/)
|
|
1048
|
+
* ]
|
|
1049
|
+
* }
|
|
1050
|
+
* });
|
|
1051
|
+
* ```
|
|
1052
|
+
*/
|
|
1053
|
+
declare const signalFormValidators: {
|
|
1054
|
+
required: ValidatorFn;
|
|
1055
|
+
maxLength: typeof maxLength;
|
|
1056
|
+
minLength: typeof minLength;
|
|
1057
|
+
min: typeof min;
|
|
1058
|
+
max: typeof max;
|
|
1059
|
+
email: ValidatorFn;
|
|
1060
|
+
pattern: typeof pattern;
|
|
1061
|
+
};
|
|
1062
|
+
|
|
1063
|
+
/**
|
|
1064
|
+
* A function interface for a signal-based notification mechanism.
|
|
1065
|
+
* Calling the function returns the current notification count.
|
|
1066
|
+
* Calling `notify()` increments the count, triggering any listeners.
|
|
1067
|
+
*/
|
|
1068
|
+
interface SignalNotifier {
|
|
1069
|
+
(): number;
|
|
1070
|
+
/**
|
|
1071
|
+
* Triggers a notification by incrementing the internal signal value.
|
|
1072
|
+
*/
|
|
1073
|
+
notify: () => void;
|
|
1074
|
+
}
|
|
1075
|
+
/**
|
|
1076
|
+
* Creates a signal-based notifier function.
|
|
1077
|
+
*
|
|
1078
|
+
* Each call to the returned function returns the current notification count.
|
|
1079
|
+
* Calling `notify()` increments the count, triggering any listeners.
|
|
1080
|
+
*
|
|
1081
|
+
* @returns {SignalNotifier} A notifier function with a `notify` method.
|
|
1082
|
+
*
|
|
1083
|
+
* @example
|
|
1084
|
+
* const notifier = signalNotifier();
|
|
1085
|
+
* effect(() => {
|
|
1086
|
+
* notifier(); // Reacts to notifications
|
|
1087
|
+
* });
|
|
1088
|
+
* notifier.notify(); // Triggers the effect
|
|
1089
|
+
*/
|
|
1090
|
+
declare function signalNotifier(): SignalNotifier;
|
|
1091
|
+
|
|
1092
|
+
/**
|
|
1093
|
+
* A writable signal enhanced with debounce capabilities.
|
|
1094
|
+
*
|
|
1095
|
+
* Extends `WritableSignal<T>` so it can be read and written like a normal signal,
|
|
1096
|
+
* but also exposes a `setDebounced` method that applies values after a configurable delay
|
|
1097
|
+
* and an `isLoading` signal that indicates whether a debounced update is pending.
|
|
1098
|
+
*
|
|
1099
|
+
* @typeParam T - The type of the signal's value.
|
|
1100
|
+
*/
|
|
1101
|
+
interface SignalDebounce<T> extends WritableSignal<T> {
|
|
1102
|
+
/** Sets the signal value after the configured debounce delay. */
|
|
1103
|
+
setDebounced(value: T): void;
|
|
1104
|
+
/** Reactive flag that is `true` while a debounced update is pending. */
|
|
1105
|
+
isLoading: Signal<boolean>;
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Creates a debounced writable signal.
|
|
1109
|
+
*
|
|
1110
|
+
* The returned signal can be written to instantly via its `WritableSignal` interface
|
|
1111
|
+
* **or** through `setDebounced(value)` which delays the commit by `debounceTime` ms.
|
|
1112
|
+
* While a debounced write is pending, `isLoading()` returns `true`.
|
|
1113
|
+
*
|
|
1114
|
+
* If a reactive `params` function is supplied, the signal will also track that
|
|
1115
|
+
* source and debounce upstream changes (requires an injection context).
|
|
1116
|
+
*
|
|
1117
|
+
* @typeParam T - The type of the signal's value.
|
|
1118
|
+
* @param options - Configuration object.
|
|
1119
|
+
* @param options.params - Optional reactive source function whose return value
|
|
1120
|
+
* is tracked and debounced into the signal.
|
|
1121
|
+
* @param options.debounceTime - Delay in milliseconds before a debounced value
|
|
1122
|
+
* is committed.
|
|
1123
|
+
* @param options.initialValue - Optional initial value for the signal.
|
|
1124
|
+
* @param options.injector - Optional Angular `Injector` to use for setting up
|
|
1125
|
+
* reactive tracking. Required if `params` is provided and this function is called
|
|
1126
|
+
* outside of an injection context.
|
|
1127
|
+
* @returns A `SignalDebounce<T>` instance.
|
|
1128
|
+
*
|
|
1129
|
+
* @example
|
|
1130
|
+
* ```ts
|
|
1131
|
+
* // Simple debounced signal with an initial value
|
|
1132
|
+
* const search = signalDebounce<string>({ debounceTime: 300, initialValue: '' });
|
|
1133
|
+
* search.setDebounced('hello'); // commits after 300 ms
|
|
1134
|
+
*
|
|
1135
|
+
* // Tracking a reactive source
|
|
1136
|
+
* const query = signal('angular');
|
|
1137
|
+
* const debounced = signalDebounce({ params: () => query(), debounceTime: 500 });
|
|
1138
|
+
* ```
|
|
1139
|
+
*/
|
|
1140
|
+
declare function signalDebounce<T>(options: {
|
|
1141
|
+
params?: () => T;
|
|
1142
|
+
debounceTime: number;
|
|
1143
|
+
initialValue?: T;
|
|
1144
|
+
injector?: unknown;
|
|
1145
|
+
}): SignalDebounce<T>;
|
|
1146
|
+
|
|
1147
|
+
/**
|
|
1148
|
+
* A reactive wrapper around a `Set` backed by Angular signals.
|
|
1149
|
+
* All mutations produce a new `Set` instance, ensuring signal-based change detection works correctly.
|
|
1150
|
+
*
|
|
1151
|
+
* @template T The type of elements stored in the set. Defaults to `number`.
|
|
1152
|
+
*/
|
|
1153
|
+
interface SignalSet<T = number> {
|
|
1154
|
+
/** The current underlying `Set` (read via signal). */
|
|
1155
|
+
(): Set<T>;
|
|
1156
|
+
/** The number of elements in the set (reactive). */
|
|
1157
|
+
size: Signal<number>;
|
|
1158
|
+
/** A computed signal that returns the set contents as an array. */
|
|
1159
|
+
toArray: Signal<T[]>;
|
|
1160
|
+
/** Adds the element if absent, removes it if present. */
|
|
1161
|
+
toggle: (id: T) => void;
|
|
1162
|
+
/** Returns `true` if the element exists in the set. */
|
|
1163
|
+
has: (id: T) => boolean;
|
|
1164
|
+
/** Adds an element to the set. */
|
|
1165
|
+
add: (id: T) => void;
|
|
1166
|
+
/** Removes an element from the set. */
|
|
1167
|
+
delete: (id: T) => void;
|
|
1168
|
+
/** Removes all elements from the set. */
|
|
1169
|
+
clear: () => void;
|
|
1170
|
+
/** Returns a human-readable string representation, e.g. `SignalSet(1, 2, 3)`. */
|
|
1171
|
+
toString: () => string;
|
|
1172
|
+
/** Converts the set to a JSON-compatible array. e.g. `[1, 2, "a"]` */
|
|
1173
|
+
toJSON: () => T[];
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Creates a reactive `SignalSet` backed by Angular signals.
|
|
1177
|
+
*
|
|
1178
|
+
* Every mutation creates a new `Set`, so Angular's signal equality check triggers updates.
|
|
1179
|
+
*
|
|
1180
|
+
* @template T The element type. Defaults to `number`.
|
|
1181
|
+
* @param initialValue An optional iterable to seed the set with.
|
|
1182
|
+
* @returns A {@link SignalSet} instance.
|
|
1183
|
+
*
|
|
1184
|
+
* @example
|
|
1185
|
+
* ```ts
|
|
1186
|
+
* const selected = signalSet<number>();
|
|
1187
|
+
* selected.add(1);
|
|
1188
|
+
* selected.toggle(2);
|
|
1189
|
+
* console.log(selected.toArray()); // [1, 2]
|
|
1190
|
+
* selected.toggle(1);
|
|
1191
|
+
* console.log(selected.has(1)); // false
|
|
1192
|
+
* ```
|
|
1193
|
+
*/
|
|
1194
|
+
declare function signalSet<T = number>(initialValue?: Iterable<T> | (() => Iterable<T>)): SignalSet<T>;
|
|
1195
|
+
|
|
1196
|
+
/**
|
|
1197
|
+
* Resolves `T` to itself when it is a valid object key (`string | number | symbol`),
|
|
1198
|
+
* otherwise falls back to `string`. Used to type-safe `Record` conversions.
|
|
1199
|
+
*/
|
|
1200
|
+
type ToKey<T> = T extends string | number | symbol ? T : string;
|
|
1201
|
+
/**
|
|
1202
|
+
* A reactive wrapper around a `Map` backed by Angular signals.
|
|
1203
|
+
* All mutations produce a new `Map` instance, ensuring signal-based change detection works correctly.
|
|
1204
|
+
*
|
|
1205
|
+
* @template K The key type. Defaults to `string`.
|
|
1206
|
+
* @template V The value type. Defaults to `unknown`.
|
|
1207
|
+
*/
|
|
1208
|
+
interface SignalMap<K = string, V = unknown> {
|
|
1209
|
+
/** The current underlying `Map` (read via signal). */
|
|
1210
|
+
(): Map<K, V>;
|
|
1211
|
+
/** The number of entries in the map (reactive). */
|
|
1212
|
+
size: Signal<number>;
|
|
1213
|
+
/** A computed signal that returns the map keys as an array. */
|
|
1214
|
+
keys: Signal<K[]>;
|
|
1215
|
+
/** A computed signal that returns the map values as an array. */
|
|
1216
|
+
values: Signal<V[]>;
|
|
1217
|
+
/** A computed signal that returns the map entries as an array of `[key, value]` tuples. */
|
|
1218
|
+
entries: Signal<[K, V][]>;
|
|
1219
|
+
/** Returns the value associated with `key`, or `undefined` if absent. */
|
|
1220
|
+
get: (key: K) => V | undefined;
|
|
1221
|
+
/** Returns `true` if the map contains the given `key`. */
|
|
1222
|
+
has: (key: K) => boolean;
|
|
1223
|
+
/** Sets (or overwrites) the value for `key` and returns the updated `Map`. */
|
|
1224
|
+
set: (key: K, value: V) => Map<K, V>;
|
|
1225
|
+
/** Removes the entry for `key`. Returns `true` if the key existed. */
|
|
1226
|
+
delete: (key: K) => boolean;
|
|
1227
|
+
/** Removes all entries from the map. */
|
|
1228
|
+
clear: () => void;
|
|
1229
|
+
/** Returns a human-readable string, e.g. `SignalMap(a => 1, b => 2)`. */
|
|
1230
|
+
toString: () => string;
|
|
1231
|
+
/** Converts the map to a JSON-compatible object. */
|
|
1232
|
+
toJSON: () => Record<ToKey<K>, V>;
|
|
1233
|
+
}
|
|
1234
|
+
/**
|
|
1235
|
+
* Creates a reactive `SignalMap` backed by Angular signals.
|
|
1236
|
+
*
|
|
1237
|
+
* Every mutation creates a new `Map`, so Angular's signal equality check triggers updates.
|
|
1238
|
+
* Accepts either an iterable of `[key, value]` pairs or a plain object as the initial value.
|
|
1239
|
+
*
|
|
1240
|
+
* @template K The key type. Defaults to `string`.
|
|
1241
|
+
* @template V The value type. Defaults to `unknown`.
|
|
1242
|
+
* @param initialValue An optional iterable of entries or a plain object to seed the map.
|
|
1243
|
+
* @returns A {@link SignalMap} instance.
|
|
1244
|
+
*
|
|
1245
|
+
* @example
|
|
1246
|
+
* ```ts
|
|
1247
|
+
* const cache = signalMap<string, number>({ a: 1, b: 2 });
|
|
1248
|
+
* cache.set('c', 3);
|
|
1249
|
+
* console.log(cache.keys()); // ['a', 'b', 'c']
|
|
1250
|
+
* cache.delete('a'); // true
|
|
1251
|
+
* console.log(cache.toJSON()); // {b:2,c:3}
|
|
1252
|
+
* ```
|
|
1253
|
+
*/
|
|
1254
|
+
declare function signalMap<K = string, V = unknown>(initialValue?: Iterable<[K, V]> | Record<ToKey<K>, V> | (() => Iterable<[K, V]> | Record<ToKey<K>, V>)): SignalMap<K, V>;
|
|
1255
|
+
|
|
1256
|
+
/**
|
|
1257
|
+
* Creates a reactive object backed by Angular signals.
|
|
1258
|
+
*
|
|
1259
|
+
* Every property is stored as a `WritableSignal`. Reading a property
|
|
1260
|
+
* (e.g. `obj.name` or `obj['name']`) calls the signal — so it's
|
|
1261
|
+
* automatically tracked in templates, `computed()`, and `effect()`.
|
|
1262
|
+
* Setting a property calls `signal.set()`, which triggers reactivity.
|
|
1263
|
+
*
|
|
1264
|
+
* @example
|
|
1265
|
+
* // In a component:
|
|
1266
|
+
* protected person = signalObject({ name: 'dvirus', age: 30 });
|
|
1267
|
+
*
|
|
1268
|
+
* // In the template (reactive — updates automatically):
|
|
1269
|
+
* // {{ person.name }}
|
|
1270
|
+
*
|
|
1271
|
+
* // In the class:
|
|
1272
|
+
* // person.name = 'changed'; → triggers re-render
|
|
1273
|
+
* // person['age'] = 31; → triggers re-render
|
|
1274
|
+
*/
|
|
1275
|
+
type SignalObject<T> = T & {
|
|
1276
|
+
/**
|
|
1277
|
+
* Call the SignalObject as a function to get a reactive snapshot.
|
|
1278
|
+
*
|
|
1279
|
+
* @example
|
|
1280
|
+
* const person = signalObject({ name: 'dvirus', age: 30 });
|
|
1281
|
+
* person(); // { name: 'dvirus', age: 30 } — tracked by Angular
|
|
1282
|
+
*/
|
|
1283
|
+
(): T;
|
|
1284
|
+
/**
|
|
1285
|
+
* A computed signal that returns a plain snapshot of all properties.
|
|
1286
|
+
* Reading this tracks ALL properties — any property change triggers reactivity.
|
|
1287
|
+
*
|
|
1288
|
+
* @example
|
|
1289
|
+
* effect(() => console.log(person.$snapshot()));
|
|
1290
|
+
* // logs whenever ANY property changes
|
|
1291
|
+
*
|
|
1292
|
+
* const label = computed(() => {
|
|
1293
|
+
* const snap = person.$snapshot();
|
|
1294
|
+
* return `${snap.name} (${snap.age})`;
|
|
1295
|
+
* });
|
|
1296
|
+
*/
|
|
1297
|
+
/**
|
|
1298
|
+
* Spreads one or more objects (plain or reactive) into this SignalObject.
|
|
1299
|
+
* Mimics `Object.assign(this, ...sources)` / `{ ...this, ...a, ...b }`.
|
|
1300
|
+
*
|
|
1301
|
+
* - Existing keys → updates the signal (triggers reactivity)
|
|
1302
|
+
* - New keys → creates a new signal and bumps the version
|
|
1303
|
+
* - Accepts plain objects and SignalObjects interchangeably
|
|
1304
|
+
*
|
|
1305
|
+
* @param sources - One or more plain objects or SignalObjects to merge in
|
|
1306
|
+
*
|
|
1307
|
+
* @example
|
|
1308
|
+
* const person = signalObject({ name: 'dvirus' });
|
|
1309
|
+
* person.$assign({ age: 30 }, otherSignalObj);
|
|
1310
|
+
*/
|
|
1311
|
+
$assign(...sources: Partial<T>[]): void;
|
|
1312
|
+
};
|
|
1313
|
+
/**
|
|
1314
|
+
* Creates a reactive object backed by Angular signals.
|
|
1315
|
+
*
|
|
1316
|
+
* Every property is stored as a `WritableSignal`. Reading a property
|
|
1317
|
+
* (e.g. `obj.name` or `obj['name']`) calls the signal — so it's
|
|
1318
|
+
* automatically tracked in templates, `computed()`, and `effect()`.
|
|
1319
|
+
* Setting a property calls `signal.set()`, which triggers reactivity.
|
|
1320
|
+
*
|
|
1321
|
+
* @param initialValue - The plain object to make reactive
|
|
1322
|
+
* @returns A `SignalObject<T>` proxy with reactive property access, `$snapshot`, and `$assign`
|
|
1323
|
+
*
|
|
1324
|
+
* @example
|
|
1325
|
+
* // In a component:
|
|
1326
|
+
* protected person = signalObject({ name: 'dvirus', age: 30 });
|
|
1327
|
+
*
|
|
1328
|
+
* // In the template (reactive — updates automatically):
|
|
1329
|
+
* // {{ person.name }}
|
|
1330
|
+
*
|
|
1331
|
+
* // In the class:
|
|
1332
|
+
* person.name = 'changed'; // triggers re-render
|
|
1333
|
+
* person['age'] = 31; // triggers re-render
|
|
1334
|
+
*
|
|
1335
|
+
* // Spread (plain snapshot):
|
|
1336
|
+
* const copy = { ...person }; // { name: 'changed', age: 31 }
|
|
1337
|
+
*
|
|
1338
|
+
* // Track all properties reactively:
|
|
1339
|
+
* effect(() => console.log(person.$snapshot()));
|
|
1340
|
+
*/
|
|
1341
|
+
declare function signalObject<T extends object>(initialValue: T): SignalObject<T>;
|
|
1342
|
+
/**
|
|
1343
|
+
* Type guard — checks if a value is a reactive SignalObject proxy.
|
|
1344
|
+
*
|
|
1345
|
+
* @example
|
|
1346
|
+
* isSignalObject(signalObject({ a: 1 })); // true
|
|
1347
|
+
* isSignalObject({ a: 1 }); // false
|
|
1348
|
+
* isSignalObject({ ...signalObject({ a: 1 }) }); // false (spread = plain copy)
|
|
1349
|
+
*/
|
|
1350
|
+
declare function isSignalObject<T extends Record<string, unknown>>(value: unknown): value is SignalObject<T>;
|
|
1351
|
+
/**
|
|
1352
|
+
* Reactively merges multiple SignalObjects (like `{ ...a, ...b }`).
|
|
1353
|
+
* Returns a `Signal` that re-evaluates whenever any source property changes.
|
|
1354
|
+
* Later sources win on key conflicts, just like spread.
|
|
1355
|
+
*
|
|
1356
|
+
* same as `computed(() => ({ ...a, ...b }))`
|
|
1357
|
+
* but with proper tracking of nested properties and support for non-reactive objects as sources.
|
|
1358
|
+
*
|
|
1359
|
+
* @example
|
|
1360
|
+
* const objA = signalObject({ name: 'dvirus', role: 'dev' });
|
|
1361
|
+
* const objB = signalObject({ age: 30, role: 'admin' });
|
|
1362
|
+
* const merged = mergeSignalObjects(objA, objB);
|
|
1363
|
+
* merged(); // { name: 'dvirus', role: 'admin', age: 30 }
|
|
1364
|
+
*/
|
|
1365
|
+
declare function mergeSignalObjects<T extends object[]>(...sources: [...{
|
|
1366
|
+
[K in keyof T]: T[K] | SignalObject<T[K]>;
|
|
1367
|
+
}]): Signal<UnionToIntersection<T[number]>>;
|
|
1368
|
+
/**
|
|
1369
|
+
* Extracts the plain object type `U` from a `SignalObject<U>`.
|
|
1370
|
+
* Returns `T` as-is if it's not a `SignalObject`.
|
|
1371
|
+
*/
|
|
1372
|
+
/**
|
|
1373
|
+
* Converts a union of types into an intersection.
|
|
1374
|
+
* Used to merge multiple object types from `mergeSignalObjects` into a single combined type.
|
|
1375
|
+
*
|
|
1376
|
+
* @example
|
|
1377
|
+
* // UnionToIntersection<{ a: 1 } | { b: 2 }> → { a: 1 } & { b: 2 }
|
|
1378
|
+
*/
|
|
1379
|
+
type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
|
1380
|
+
|
|
1381
|
+
interface ControlEvent<T> {
|
|
1382
|
+
/**
|
|
1383
|
+
* Form control from which this event is originated.
|
|
1384
|
+
*
|
|
1385
|
+
* Note: the type of the control can't be inferred from T as the event can be emitted by any of child controls
|
|
1386
|
+
*/
|
|
1387
|
+
readonly source: AbstractControl<unknown | T>;
|
|
1388
|
+
}
|
|
1389
|
+
/**
|
|
1390
|
+
* A reactive signal-based wrapper around an `AbstractControl`, exposing
|
|
1391
|
+
* the control's state (value, status, touched, dirty, errors, etc.) as
|
|
1392
|
+
* Angular signals for use in templates and computed expressions.
|
|
1393
|
+
*
|
|
1394
|
+
* @template T - The value type of the underlying form control.
|
|
1395
|
+
* @template TControl - The specific `AbstractControl` type being wrapped (e.g., `FormControl`, `FormGroup`, `FormArray`).
|
|
1396
|
+
*/
|
|
1397
|
+
interface ControlSignal<T, TControl extends AbstractControl<unknown> = AbstractControl<T>> {
|
|
1398
|
+
/** Signal returning the underlying `AbstractControl` instance with its specific type. */
|
|
1399
|
+
control: TControl;
|
|
1400
|
+
/** Signal returning the current value of the control. */
|
|
1401
|
+
value: Signal<T | null | undefined>;
|
|
1402
|
+
/** Signal returning the current validation status (`VALID`, `INVALID`, `PENDING`, `DISABLED`). */
|
|
1403
|
+
status: Signal<FormControlStatus | null | undefined>;
|
|
1404
|
+
/** Signal returning the most recent `ControlEvent` emitted by the control. */
|
|
1405
|
+
events: Signal<ControlEvent<T> | null | undefined>;
|
|
1406
|
+
/** Signal that is `true` when the control status is `DISABLED`. */
|
|
1407
|
+
disabled: Signal<boolean>;
|
|
1408
|
+
/** Signal that is `true` when the control status is `VALID`. */
|
|
1409
|
+
valid: Signal<boolean>;
|
|
1410
|
+
/** Signal that is `true` when the control status is `INVALID`. */
|
|
1411
|
+
invalid: Signal<boolean>;
|
|
1412
|
+
/** Signal that is `true` when the control has been touched. */
|
|
1413
|
+
touched: Signal<boolean>;
|
|
1414
|
+
/** Signal that is `true` when the control is dirty. */
|
|
1415
|
+
dirty: Signal<boolean>;
|
|
1416
|
+
/** Signal returning the current validation errors, or `null` if there are none. */
|
|
1417
|
+
errors: Signal<ValidationErrors | null>;
|
|
1418
|
+
/** Signal returning the key of the first validation error, or `null`. */
|
|
1419
|
+
firstErrorKey: Signal<string | null>;
|
|
1420
|
+
/** Signal that is `true` when the control is both touched and invalid. */
|
|
1421
|
+
touchedAndInvalid: Signal<boolean>;
|
|
1422
|
+
/**
|
|
1423
|
+
* Signal returning the control's synchronous validators as a `ValidationErrors`-like object, or `null` if there are none.
|
|
1424
|
+
* @Note that this does not include asynchronous validators, and the shape of the returned object may differ from the actual validation errors emitted by the control.
|
|
1425
|
+
*/
|
|
1426
|
+
validators: Signal<ValidationErrors | null>;
|
|
1427
|
+
/** Tears down all internal subscriptions and effects. */
|
|
1428
|
+
unsubscribe(): void;
|
|
1429
|
+
}
|
|
1430
|
+
/**
|
|
1431
|
+
* Creates a {@link ControlSignal} that mirrors an `AbstractControl`'s
|
|
1432
|
+
* reactive state as Angular signals.
|
|
1433
|
+
*
|
|
1434
|
+
* **Important:** If called outside an injection context, you must manually handle
|
|
1435
|
+
* un-subscription by either:
|
|
1436
|
+
* - Calling the `unsubscribe()` method on the returned {@link ControlSignal}, or
|
|
1437
|
+
* - Passing a `DestroyRef` via the `options` parameter for automatic cleanup.
|
|
1438
|
+
*
|
|
1439
|
+
* If called within an injection context without providing a `DestroyRef`, the
|
|
1440
|
+
* subscriptions will be automatically cleaned up on component destruction.
|
|
1441
|
+
*
|
|
1442
|
+
* @template T - The value type of the form control.
|
|
1443
|
+
* @param control - The form control or a signal wrapping one.
|
|
1444
|
+
* @param options - Optional configuration object containing:
|
|
1445
|
+
* - `destroyRef`: A `DestroyRef` used for automatic cleanup on destruction.
|
|
1446
|
+
* If not provided, the function will attempt to inject one from the current
|
|
1447
|
+
* injection context. If neither is available, manual un-subscription is required.
|
|
1448
|
+
* @returns A {@link ControlSignal} exposing the control's state as signals.
|
|
1449
|
+
* @throws If `control` is a signal and no `Injector` is available.
|
|
1450
|
+
*/
|
|
1451
|
+
declare function controlSignal<T, TControl extends AbstractControl<unknown> = FormControl<T>>(control: TControl, options?: {
|
|
1452
|
+
destroyRef?: DestroyRef | null;
|
|
1453
|
+
}): ControlSignal<T, TControl>;
|
|
1454
|
+
/**
|
|
1455
|
+
* Extends {@link ControlSignal} with a strongly-typed `controls` map,
|
|
1456
|
+
* providing a `ControlSignal` for every control in the `FormGroup`.
|
|
1457
|
+
*
|
|
1458
|
+
* @template T - An object type whose keys correspond to the group's control names
|
|
1459
|
+
* and whose values are the respective control value types.
|
|
1460
|
+
*/
|
|
1461
|
+
interface FormGroupSignal<TControls extends TypedControlMap> extends ControlSignal<ControlsValue<TControls>, FormGroup<TControls>> {
|
|
1462
|
+
/** The underlying `FormGroup` instance. */
|
|
1463
|
+
control: FormGroup<TControls>;
|
|
1464
|
+
/** A map of child control names to their individual {@link ControlSignal} instances. */
|
|
1465
|
+
controls: {
|
|
1466
|
+
[K in keyof TControls]: NestedControlSignal<TControls[K]>;
|
|
1467
|
+
};
|
|
1468
|
+
}
|
|
1469
|
+
/**
|
|
1470
|
+
* Creates a {@link FormGroupSignal} for a `FormGroup`. Child controls are
|
|
1471
|
+
* converted recursively, so nested `FormGroup` and `FormArray` structures
|
|
1472
|
+
* are supported.
|
|
1473
|
+
*
|
|
1474
|
+
* @template TControls - A typed map of control names to `AbstractControl` instances.
|
|
1475
|
+
* @param formGroup - The `FormGroup` to wrap.
|
|
1476
|
+
* @param options - Optional `DestroyRef` and `Injector` forwarded to
|
|
1477
|
+
* each underlying {@link controlSignal} call.
|
|
1478
|
+
* @returns A {@link FormGroupSignal} with both group-level and per-control signals.
|
|
1479
|
+
*/
|
|
1480
|
+
declare function formGroupSignal<TControls extends TypedControlMap>(formGroup: FormGroup<TControls>, options?: {
|
|
1481
|
+
destroyRef?: DestroyRef | null;
|
|
1482
|
+
}): FormGroupSignal<TControls>;
|
|
1483
|
+
interface FormArraySignal<TControl extends AbstractControl<unknown>> extends ControlSignal<ControlValue<TControl>[], FormArray<TControl>> {
|
|
1484
|
+
/** The underlying `FormArray` instance. */
|
|
1485
|
+
control: FormArray<TControl>;
|
|
1486
|
+
controls: NestedControlSignal<TControl>[];
|
|
1487
|
+
}
|
|
1488
|
+
/**
|
|
1489
|
+
* Creates a {@link FormArraySignal} for a `FormArray`. Child controls are
|
|
1490
|
+
* converted recursively, so arrays of `FormGroup`, arrays of `FormArray`,
|
|
1491
|
+
* and arrays of `FormControl` are all supported.
|
|
1492
|
+
*/
|
|
1493
|
+
declare function formArraySignal<TControl extends AbstractControl<unknown>>(formArray: FormArray<TControl>, options?: {
|
|
1494
|
+
destroyRef?: DestroyRef | null;
|
|
1495
|
+
}): FormArraySignal<TControl>;
|
|
1496
|
+
type TypedControlMap = Record<string, AbstractControl<unknown>>;
|
|
1497
|
+
type ControlValue<TControl extends AbstractControl<unknown>> = TControl extends AbstractControl<infer TValue> ? TValue : never;
|
|
1498
|
+
type ControlsValue<TControls extends TypedControlMap> = {
|
|
1499
|
+
[K in keyof TControls]: ControlValue<TControls[K]>;
|
|
1500
|
+
};
|
|
1501
|
+
type NestedControlSignal<TControl extends AbstractControl<unknown>> = TControl extends FormGroup<infer TControls> ? FormGroupSignal<TControls> : TControl extends FormArray<infer TItemControl> ? FormArraySignal<TItemControl & AbstractControl<unknown>> : ControlSignal<ControlValue<TControl>, TControl>;
|
|
1502
|
+
|
|
1503
|
+
/**
|
|
1504
|
+
* Types for the result tuple with discriminated union
|
|
1505
|
+
* @template T - Type of the successful result
|
|
1506
|
+
* @template E - Type of the error, defaults to Error
|
|
1507
|
+
*/
|
|
1508
|
+
type TryResult<T, E = Error> = [T, null] | [null, E];
|
|
1509
|
+
/**
|
|
1510
|
+
* Main wrapper function to handle promise with try-catch
|
|
1511
|
+
* @template T - Type of the successful result
|
|
1512
|
+
* @template E - Type of the error, defaults to Error
|
|
1513
|
+
* @param {()=> T} fn - The function to handle
|
|
1514
|
+
* @returns {TryResult<T, E>} - A tuple with either the result or the error
|
|
1515
|
+
*/
|
|
1516
|
+
declare function tryCatch<T, E = Error>(fn: () => T): TryResult<T, E>;
|
|
1517
|
+
|
|
1518
|
+
/**
|
|
1519
|
+
* Type for value equality comparison function
|
|
1520
|
+
*/
|
|
1521
|
+
type ValueEqualityFn<T> = (a: T, b: T) => boolean;
|
|
1522
|
+
/**
|
|
1523
|
+
* Creates a `WritableSignal` whose value is derived from a computation function,
|
|
1524
|
+
* but can also be overridden manually via `.set()` / `.update()`.
|
|
1525
|
+
* When the reactive dependencies inside the computation change, the signal resets to the new derived value.
|
|
1526
|
+
*
|
|
1527
|
+
* Angular-16 compatible alternative to `linkedSignal` (Angular 19+).
|
|
1528
|
+
*
|
|
1529
|
+
* @overload
|
|
1530
|
+
* @param computation - A computation function that returns the derived value
|
|
1531
|
+
* @param options - Optional configuration (equal, debugName)
|
|
1532
|
+
* @returns A WritableSignal<D>
|
|
1533
|
+
*/
|
|
1534
|
+
declare function writableSignal<D>(computation: () => D, options?: {
|
|
1535
|
+
equal?: ValueEqualityFn<D>;
|
|
1536
|
+
debugName?: string;
|
|
1537
|
+
}): WritableSignal<D>;
|
|
1538
|
+
/**
|
|
1539
|
+
* Creates a `WritableSignal` with explicit source tracking and computation.
|
|
1540
|
+
* The `source` function provides the reactive dependencies, and the `computation` function derives the value from the source.
|
|
1541
|
+
* Manual overrides via `.set()` / `.update()` are only valid for the specific source state they were set against.
|
|
1542
|
+
*
|
|
1543
|
+
* Angular-16 compatible alternative to `linkedSignal` (Angular 19+).
|
|
1544
|
+
*
|
|
1545
|
+
* @overload
|
|
1546
|
+
* @param options - Configuration object with source tracking and computation
|
|
1547
|
+
* @returns A WritableSignal<D>
|
|
1548
|
+
*/
|
|
1549
|
+
declare function writableSignal<S, D>(options: {
|
|
1550
|
+
source: () => S;
|
|
1551
|
+
computation: (source: S, previous?: {
|
|
1552
|
+
source: S;
|
|
1553
|
+
value: D;
|
|
1554
|
+
}) => D;
|
|
1555
|
+
equal?: ValueEqualityFn<D>;
|
|
1556
|
+
debugName?: string;
|
|
1557
|
+
}): WritableSignal<D>;
|
|
1558
|
+
|
|
1559
|
+
export { controlSignal, createSignalFormArray, createSignalFormControl, createSignalFormGroup, formArray, formArraySignal, formControl, formGroup, formGroupSignal, fromSignalObj, isSignalFormArray, isSignalFormControl, isSignalFormGroup, isSignalObject, mergeSignalObjects, signalDebounce, signalForm, signalFormValidators, signalMap, signalNotifier, signalObject, signalOrFunction, signalOrValue, signalSet, toSignalObj, tryCatch, writableSignal };
|
|
1560
|
+
export type { ControlSignal, FirstError, FormArraySignal, FormGroupSignal, NestedControlSignal, ObjectType, SignalDebounce, SignalForm, SignalFormArray, SignalFormArrayInput, SignalFormContext, SignalFormControl, SignalFormControlConfig, SignalFormControlInput, SignalFormControlLike, SignalFormDisableOptions, SignalFormDisabledFn, SignalFormErrorFor, SignalFormGroupInput, SignalFormInput, SignalFormInputs, SignalFormSetValueOptions, SignalFormValidationError, SignalFormValidatorFn, SignalFormValueFor, SignalMap, SignalNotifier, SignalObj, SignalObjWritable, SignalObject, SignalOrValue, SignalOrValueObj, SignalSet };
|