@angular/forms 21.2.0-next.1 → 21.2.0-next.2
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.
- package/fesm2022/_structure-chunk.mjs +116 -26
- package/fesm2022/_structure-chunk.mjs.map +1 -1
- package/fesm2022/forms.mjs +128 -128
- package/fesm2022/forms.mjs.map +1 -1
- package/fesm2022/signals-compat.mjs +386 -47
- package/fesm2022/signals-compat.mjs.map +1 -1
- package/fesm2022/signals.mjs +402 -68
- package/fesm2022/signals.mjs.map +1 -1
- package/package.json +4 -4
- package/resources/code-examples.db +0 -0
- package/types/_structure-chunk.d.ts +253 -1039
- package/types/forms.d.ts +1 -1
- package/types/signals-compat.d.ts +131 -8
- package/types/signals.d.ts +6 -41
|
@@ -1,13 +1,64 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Angular v21.2.0-next.
|
|
2
|
+
* @license Angular v21.2.0-next.2
|
|
3
3
|
* (c) 2010-2026 Google LLC. https://angular.dev/
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { FormGroup, FormArray, AbstractControl } from '@angular/forms';
|
|
7
8
|
import { untracked, ɵRuntimeError as _RuntimeError, computed, runInInjectionContext, Injector, linkedSignal, signal, APP_ID, effect, inject } from '@angular/core';
|
|
8
|
-
import { AbstractControl } from '@angular/forms';
|
|
9
9
|
import { SIGNAL } from '@angular/core/primitives/signals';
|
|
10
10
|
|
|
11
|
+
class CompatValidationError {
|
|
12
|
+
kind = 'compat';
|
|
13
|
+
control;
|
|
14
|
+
fieldTree;
|
|
15
|
+
context;
|
|
16
|
+
message;
|
|
17
|
+
constructor({
|
|
18
|
+
context,
|
|
19
|
+
kind,
|
|
20
|
+
control
|
|
21
|
+
}) {
|
|
22
|
+
this.context = context;
|
|
23
|
+
this.kind = kind;
|
|
24
|
+
this.control = control;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function signalErrorsToValidationErrors(errors) {
|
|
28
|
+
if (errors.length === 0) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const errObj = {};
|
|
32
|
+
for (const error of errors) {
|
|
33
|
+
errObj[error.kind] = error instanceof CompatValidationError ? error.context : error;
|
|
34
|
+
}
|
|
35
|
+
return errObj;
|
|
36
|
+
}
|
|
37
|
+
function reactiveErrorsToSignalErrors(errors, control) {
|
|
38
|
+
if (errors === null) {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
return Object.entries(errors).map(([kind, context]) => {
|
|
42
|
+
return new CompatValidationError({
|
|
43
|
+
context,
|
|
44
|
+
kind,
|
|
45
|
+
control
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function extractNestedReactiveErrors(control) {
|
|
50
|
+
const errors = [];
|
|
51
|
+
if (control.errors) {
|
|
52
|
+
errors.push(...reactiveErrorsToSignalErrors(control.errors, control));
|
|
53
|
+
}
|
|
54
|
+
if (control instanceof FormGroup || control instanceof FormArray) {
|
|
55
|
+
for (const c of Object.values(control.controls)) {
|
|
56
|
+
errors.push(...extractNestedReactiveErrors(c));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return errors;
|
|
60
|
+
}
|
|
61
|
+
|
|
11
62
|
let boundPathDepth = 0;
|
|
12
63
|
function getBoundPathDepth() {
|
|
13
64
|
return boundPathDepth;
|
|
@@ -1186,6 +1237,7 @@ class FieldNode {
|
|
|
1186
1237
|
nodeState;
|
|
1187
1238
|
submitState;
|
|
1188
1239
|
fieldAdapter;
|
|
1240
|
+
controlValue;
|
|
1189
1241
|
_context = undefined;
|
|
1190
1242
|
get context() {
|
|
1191
1243
|
return this._context ??= new FieldNodeContext(this);
|
|
@@ -1200,6 +1252,7 @@ class FieldNode {
|
|
|
1200
1252
|
this.nodeState = this.fieldAdapter.createNodeState(this, options);
|
|
1201
1253
|
this.metadataState = new FieldMetadataState(this);
|
|
1202
1254
|
this.submitState = new FieldSubmitState(this);
|
|
1255
|
+
this.controlValue = this.controlValueSignal();
|
|
1203
1256
|
}
|
|
1204
1257
|
focusBoundControl(options) {
|
|
1205
1258
|
this.getBindingForFocus()?.focus(options);
|
|
@@ -1225,12 +1278,6 @@ class FieldNode {
|
|
|
1225
1278
|
get value() {
|
|
1226
1279
|
return this.structure.value;
|
|
1227
1280
|
}
|
|
1228
|
-
_controlValue = linkedSignal(() => this.value(), ...(ngDevMode ? [{
|
|
1229
|
-
debugName: "_controlValue"
|
|
1230
|
-
}] : []));
|
|
1231
|
-
get controlValue() {
|
|
1232
|
-
return this._controlValue.asReadonly();
|
|
1233
|
-
}
|
|
1234
1281
|
get keyInParent() {
|
|
1235
1282
|
return this.structure.keyInParent;
|
|
1236
1283
|
}
|
|
@@ -1312,6 +1359,12 @@ class FieldNode {
|
|
|
1312
1359
|
markAsDirty() {
|
|
1313
1360
|
this.nodeState.markAsDirty();
|
|
1314
1361
|
}
|
|
1362
|
+
markAsPristine() {
|
|
1363
|
+
this.nodeState.markAsPristine();
|
|
1364
|
+
}
|
|
1365
|
+
markAsUntouched() {
|
|
1366
|
+
this.nodeState.markAsUntouched();
|
|
1367
|
+
}
|
|
1315
1368
|
reset(value) {
|
|
1316
1369
|
untracked(() => this._reset(value));
|
|
1317
1370
|
}
|
|
@@ -1325,12 +1378,25 @@ class FieldNode {
|
|
|
1325
1378
|
child._reset();
|
|
1326
1379
|
}
|
|
1327
1380
|
}
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1381
|
+
controlValueSignal() {
|
|
1382
|
+
const controlValue = linkedSignal(this.value, ...(ngDevMode ? [{
|
|
1383
|
+
debugName: "controlValue"
|
|
1384
|
+
}] : []));
|
|
1385
|
+
const {
|
|
1386
|
+
set,
|
|
1387
|
+
update
|
|
1388
|
+
} = controlValue;
|
|
1389
|
+
controlValue.set = newValue => {
|
|
1390
|
+
set(newValue);
|
|
1331
1391
|
this.markAsDirty();
|
|
1332
1392
|
this.debounceSync();
|
|
1333
|
-
}
|
|
1393
|
+
};
|
|
1394
|
+
controlValue.update = updateFn => {
|
|
1395
|
+
update(updateFn);
|
|
1396
|
+
this.markAsDirty();
|
|
1397
|
+
this.debounceSync();
|
|
1398
|
+
};
|
|
1399
|
+
return controlValue;
|
|
1334
1400
|
}
|
|
1335
1401
|
sync() {
|
|
1336
1402
|
this.value.set(this.controlValue());
|
|
@@ -1343,8 +1409,10 @@ class FieldNode {
|
|
|
1343
1409
|
}
|
|
1344
1410
|
}
|
|
1345
1411
|
async debounceSync() {
|
|
1346
|
-
|
|
1347
|
-
|
|
1412
|
+
const debouncer = untracked(() => {
|
|
1413
|
+
this.pendingSync()?.abort();
|
|
1414
|
+
return this.nodeState.debouncer();
|
|
1415
|
+
});
|
|
1348
1416
|
if (debouncer) {
|
|
1349
1417
|
const controller = new AbortController();
|
|
1350
1418
|
const promise = debouncer(controller.signal);
|
|
@@ -1502,9 +1570,11 @@ class BasicFieldAdapter {
|
|
|
1502
1570
|
class FormFieldManager {
|
|
1503
1571
|
injector;
|
|
1504
1572
|
rootName;
|
|
1505
|
-
|
|
1573
|
+
submitOptions;
|
|
1574
|
+
constructor(injector, rootName, submitOptions) {
|
|
1506
1575
|
this.injector = injector;
|
|
1507
1576
|
this.rootName = rootName ?? `${this.injector.get(APP_ID)}.form${nextFormId++}`;
|
|
1577
|
+
this.submitOptions = submitOptions;
|
|
1508
1578
|
}
|
|
1509
1579
|
structures = new Set();
|
|
1510
1580
|
createFieldManagementEffect(root) {
|
|
@@ -1552,7 +1622,7 @@ function form(...args) {
|
|
|
1552
1622
|
const [model, schema, options] = normalizeFormArgs(args);
|
|
1553
1623
|
const injector = options?.injector ?? inject(Injector);
|
|
1554
1624
|
const pathNode = runInInjectionContext(injector, () => SchemaImpl.rootCompile(schema));
|
|
1555
|
-
const fieldManager = new FormFieldManager(injector, options?.name);
|
|
1625
|
+
const fieldManager = new FormFieldManager(injector, options?.name, options?.submission);
|
|
1556
1626
|
const adapter = options?.adapter ?? new BasicFieldAdapter();
|
|
1557
1627
|
const fieldRoot = FieldNode.newRoot(fieldManager, model, pathNode, adapter);
|
|
1558
1628
|
fieldManager.createFieldManagementEffect(fieldRoot.structure);
|
|
@@ -1581,19 +1651,39 @@ function applyWhenValue(path, predicate, schema) {
|
|
|
1581
1651
|
value
|
|
1582
1652
|
}) => predicate(value()), schema);
|
|
1583
1653
|
}
|
|
1584
|
-
async function submit(form,
|
|
1654
|
+
async function submit(form, options) {
|
|
1585
1655
|
const node = form();
|
|
1586
|
-
const
|
|
1656
|
+
const opts = typeof options === 'function' ? {
|
|
1657
|
+
action: options
|
|
1658
|
+
} : {
|
|
1659
|
+
...(node.structure.fieldManager.submitOptions ?? {}),
|
|
1660
|
+
...(options ?? {})
|
|
1661
|
+
};
|
|
1662
|
+
const action = opts?.action;
|
|
1663
|
+
if (!action) {
|
|
1664
|
+
throw new _RuntimeError(1915, ngDevMode && 'Cannot submit form with no submit action. Specify the action when creating the form, or as an additional argument to `submit()`.');
|
|
1665
|
+
}
|
|
1666
|
+
const onInvalid = opts?.onInvalid;
|
|
1667
|
+
const ignoreValidators = opts?.ignoreValidators ?? 'pending';
|
|
1668
|
+
let shouldRunAction = true;
|
|
1669
|
+
untracked(() => {
|
|
1587
1670
|
markAllAsTouched(node);
|
|
1588
|
-
|
|
1671
|
+
if (ignoreValidators === 'none') {
|
|
1672
|
+
shouldRunAction = node.valid();
|
|
1673
|
+
} else if (ignoreValidators === 'pending') {
|
|
1674
|
+
shouldRunAction = !node.invalid();
|
|
1675
|
+
}
|
|
1589
1676
|
});
|
|
1590
|
-
if (invalid) {
|
|
1591
|
-
return;
|
|
1592
|
-
}
|
|
1593
|
-
node.submitState.selfSubmitting.set(true);
|
|
1594
1677
|
try {
|
|
1595
|
-
|
|
1596
|
-
|
|
1678
|
+
if (shouldRunAction) {
|
|
1679
|
+
node.submitState.selfSubmitting.set(true);
|
|
1680
|
+
const errors = await untracked(() => action?.(form));
|
|
1681
|
+
errors && setSubmissionErrors(node, errors);
|
|
1682
|
+
return !errors || isArray(errors) && errors.length === 0;
|
|
1683
|
+
} else {
|
|
1684
|
+
untracked(() => onInvalid?.(form));
|
|
1685
|
+
}
|
|
1686
|
+
return false;
|
|
1597
1687
|
} finally {
|
|
1598
1688
|
node.submitState.selfSubmitting.set(false);
|
|
1599
1689
|
}
|
|
@@ -1630,5 +1720,5 @@ function markAllAsTouched(node) {
|
|
|
1630
1720
|
}
|
|
1631
1721
|
}
|
|
1632
1722
|
|
|
1633
|
-
export { BasicFieldAdapter, DEBOUNCER, FieldNode, FieldNodeState, FieldNodeStructure, FieldPathNode, MAX, MAX_LENGTH, MIN, MIN_LENGTH, MetadataKey, MetadataReducer, PATTERN, REQUIRED, addDefaultField, apply, applyEach, applyWhen, applyWhenValue, assertPathIsCurrent, calculateValidationSelfStatus, createManagedMetadataKey, createMetadataKey, form, getInjectorFromOptions, metadata, normalizeFormArgs, schema, submit };
|
|
1723
|
+
export { BasicFieldAdapter, CompatValidationError, DEBOUNCER, FieldNode, FieldNodeState, FieldNodeStructure, FieldPathNode, MAX, MAX_LENGTH, MIN, MIN_LENGTH, MetadataKey, MetadataReducer, PATTERN, REQUIRED, addDefaultField, apply, applyEach, applyWhen, applyWhenValue, assertPathIsCurrent, calculateValidationSelfStatus, createManagedMetadataKey, createMetadataKey, extractNestedReactiveErrors, form, getInjectorFromOptions, metadata, normalizeFormArgs, schema, signalErrorsToValidationErrors, submit };
|
|
1634
1724
|
//# sourceMappingURL=_structure-chunk.mjs.map
|