@angular/forms 22.0.0-next.3 → 22.0.0-next.5

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,5 +1,5 @@
1
1
  /**
2
- * @license Angular v22.0.0-next.3
2
+ * @license Angular v22.0.0-next.5
3
3
  * (c) 2010-2026 Google LLC. https://angular.dev/
4
4
  * License: MIT
5
5
  */
@@ -58,6 +58,9 @@ class AbstractLogic {
58
58
  const fns = this.predicates ? other.fns.map(fn => wrapWithPredicates(this.predicates, fn)) : other.fns;
59
59
  this.fns.push(...fns);
60
60
  }
61
+ hasRules() {
62
+ return this.fns.length > 0;
63
+ }
61
64
  }
62
65
  class BooleanOrLogic extends AbstractLogic {
63
66
  get defaultValue() {
@@ -162,9 +165,15 @@ class LogicContainer {
162
165
  this.syncTreeErrors = ArrayMergeIgnoreLogic.ignoreNull(predicates);
163
166
  this.asyncErrors = ArrayMergeIgnoreLogic.ignoreNull(predicates);
164
167
  }
168
+ hasAnyLogic() {
169
+ return this.hidden.hasRules() || this.disabledReasons.hasRules() || this.readonly.hasRules() || this.syncErrors.hasRules() || this.syncTreeErrors.hasRules() || this.asyncErrors.hasRules() || this.metadata.size > 0;
170
+ }
165
171
  hasMetadata(key) {
166
172
  return this.metadata.has(key);
167
173
  }
174
+ hasMetadataKeys() {
175
+ return this.metadata.size > 0;
176
+ }
168
177
  getMetadataKeys() {
169
178
  return this.metadata.keys();
170
179
  }
@@ -241,6 +250,14 @@ class LogicNodeBuilder extends AbstractLogicNodeBuilder {
241
250
  builder: subBuilder
242
251
  }) => subBuilder.hasLogic(builder));
243
252
  }
253
+ hasRules() {
254
+ return this.all.length > 0;
255
+ }
256
+ anyChildHasLogic() {
257
+ return this.all.some(({
258
+ builder
259
+ }) => builder.anyChildHasLogic());
260
+ }
244
261
  mergeIn(other, predicate) {
245
262
  if (predicate) {
246
263
  this.all.push({
@@ -306,6 +323,17 @@ class NonMergeableLogicNodeBuilder extends AbstractLogicNodeBuilder {
306
323
  hasLogic(builder) {
307
324
  return this === builder;
308
325
  }
326
+ hasRules() {
327
+ return this.logic.hasAnyLogic() || this.children.size > 0;
328
+ }
329
+ anyChildHasLogic() {
330
+ for (const child of this.children.values()) {
331
+ if (child.hasRules()) {
332
+ return true;
333
+ }
334
+ }
335
+ return false;
336
+ }
309
337
  }
310
338
  class LeafLogicNode {
311
339
  builder;
@@ -337,7 +365,16 @@ class LeafLogicNode {
337
365
  }
338
366
  }
339
367
  hasLogic(builder) {
340
- return this.builder?.hasLogic(builder) ?? false;
368
+ if (!this.builder) {
369
+ return false;
370
+ }
371
+ return this.builder.hasLogic(builder);
372
+ }
373
+ hasRules() {
374
+ return this.builder ? this.builder.hasRules() : false;
375
+ }
376
+ anyChildHasLogic() {
377
+ return this.builder ? this.builder.anyChildHasLogic() : false;
341
378
  }
342
379
  }
343
380
  class CompositeLogicNode {
@@ -356,6 +393,12 @@ class CompositeLogicNode {
356
393
  hasLogic(builder) {
357
394
  return this.all.some(node => node.hasLogic(builder));
358
395
  }
396
+ hasRules() {
397
+ return this.all.some(node => node.hasRules());
398
+ }
399
+ anyChildHasLogic() {
400
+ return this.all.some(child => child.anyChildHasLogic());
401
+ }
359
402
  }
360
403
  function getAllChildBuilders(builder, key) {
361
404
  if (builder instanceof LogicNodeBuilder) {
@@ -568,10 +611,12 @@ function override(getInitial) {
568
611
  getInitial: () => getInitial?.()
569
612
  };
570
613
  }
614
+ const IS_ASYNC_VALIDATION_RESOURCE = Symbol('IS_ASYNC_VALIDATION_RESOURCE');
571
615
  class MetadataKey {
572
616
  reducer;
573
617
  create;
574
618
  brand;
619
+ [IS_ASYNC_VALIDATION_RESOURCE];
575
620
  constructor(reducer, create) {
576
621
  this.reducer = reducer;
577
622
  this.create = create;
@@ -732,6 +777,8 @@ class FieldNodeContext {
732
777
  cache = new WeakMap();
733
778
  constructor(node) {
734
779
  this.node = node;
780
+ this.fieldTreeOf = this.fieldTreeOf.bind(this);
781
+ this.stateOf = this.stateOf.bind(this);
735
782
  }
736
783
  resolve(target) {
737
784
  if (!this.cache.has(target)) {
@@ -784,8 +831,12 @@ class FieldNodeContext {
784
831
  }, ...(ngDevMode ? [{
785
832
  debugName: "index"
786
833
  }] : []));
787
- fieldTreeOf = p => this.resolve(p);
788
- stateOf = p => this.resolve(p)();
834
+ fieldTreeOf(p) {
835
+ return this.resolve(p);
836
+ }
837
+ stateOf(p) {
838
+ return this.resolve(p)();
839
+ }
789
840
  valueOf = p => {
790
841
  const result = this.resolve(p)().value();
791
842
  if (result instanceof AbstractControl) {
@@ -800,13 +851,20 @@ class FieldMetadataState {
800
851
  metadata = new Map();
801
852
  constructor(node) {
802
853
  this.node = node;
803
- for (const key of this.node.logicNode.logic.getMetadataKeys()) {
804
- if (key.create) {
805
- const logic = this.node.logicNode.logic.getMetadata(key);
806
- const result = untracked(() => runInInjectionContext(this.node.structure.injector, () => key.create(computed(() => logic.compute(this.node.context)))));
807
- this.metadata.set(key, result);
808
- }
854
+ }
855
+ runMetadataCreateLifecycle() {
856
+ if (!this.node.logicNode.logic.hasMetadataKeys()) {
857
+ return;
809
858
  }
859
+ untracked(() => runInInjectionContext(this.node.structure.injector, () => {
860
+ for (const key of this.node.logicNode.logic.getMetadataKeys()) {
861
+ if (key.create) {
862
+ const logic = this.node.logicNode.logic.getMetadata(key);
863
+ const result = key.create(this.node, computed(() => logic.compute(this.node.context)));
864
+ this.metadata.set(key, result);
865
+ }
866
+ }
867
+ }));
810
868
  }
811
869
  get(key) {
812
870
  if (this.has(key)) {
@@ -900,6 +958,7 @@ class FieldNodeStructure {
900
958
  createChildNode;
901
959
  identitySymbol = Symbol();
902
960
  _injector = undefined;
961
+ _anyChildHasLogic;
903
962
  get injector() {
904
963
  this._injector ??= Injector.create({
905
964
  providers: [],
@@ -913,13 +972,26 @@ class FieldNodeStructure {
913
972
  this.createChildNode = createChildNode;
914
973
  }
915
974
  children() {
975
+ this.ensureChildrenMap();
916
976
  const map = this.childrenMap();
917
977
  if (map === undefined) {
918
978
  return [];
919
979
  }
920
980
  return Array.from(map.byPropertyKey.values()).map(child => untracked(child.reader));
921
981
  }
982
+ _areChildrenMaterialized() {
983
+ return untracked(this.childrenMap) !== undefined;
984
+ }
985
+ ensureChildrenMap() {
986
+ if (this._areChildrenMaterialized()) {
987
+ return;
988
+ }
989
+ untracked(() => {
990
+ this.childrenMap.update(current => this.computeChildrenMap(this.value(), current, true));
991
+ });
992
+ }
922
993
  getChild(key) {
994
+ this.ensureChildrenMap();
923
995
  const strKey = key.toString();
924
996
  let reader = untracked(this.childrenMap)?.byPropertyKey.get(strKey)?.reader;
925
997
  if (!reader) {
@@ -980,67 +1052,73 @@ class FieldNodeStructure {
980
1052
  createChildrenMap() {
981
1053
  return linkedSignal({
982
1054
  source: this.value,
983
- computation: (value, previous) => {
984
- if (!isObject(value)) {
985
- return undefined;
986
- }
987
- const prevData = previous?.value ?? {
988
- byPropertyKey: new Map()
989
- };
990
- let data;
991
- const parentIsArray = isArray(value);
992
- if (prevData !== undefined) {
993
- if (parentIsArray) {
994
- data = maybeRemoveStaleArrayFields(prevData, value, this.identitySymbol);
995
- } else {
996
- data = maybeRemoveStaleObjectFields(prevData, value);
997
- }
1055
+ computation: (value, previous) => this.computeChildrenMap(value, previous?.value, false)
1056
+ });
1057
+ }
1058
+ computeChildrenMap(value, prevData, forceMaterialize) {
1059
+ if (!isObject(value)) {
1060
+ return undefined;
1061
+ }
1062
+ if (!forceMaterialize && prevData === undefined) {
1063
+ if (!(this._anyChildHasLogic ??= this.logic.anyChildHasLogic())) {
1064
+ return undefined;
1065
+ }
1066
+ }
1067
+ prevData ??= {
1068
+ byPropertyKey: new Map()
1069
+ };
1070
+ let materializedChildren;
1071
+ const parentIsArray = isArray(value);
1072
+ if (prevData !== undefined) {
1073
+ if (parentIsArray) {
1074
+ materializedChildren = maybeRemoveStaleArrayFields(prevData, value, this.identitySymbol);
1075
+ } else {
1076
+ materializedChildren = maybeRemoveStaleObjectFields(prevData, value);
1077
+ }
1078
+ }
1079
+ for (const key of Object.keys(value)) {
1080
+ let trackingKey = undefined;
1081
+ const childValue = value[key];
1082
+ if (childValue === undefined) {
1083
+ if (prevData.byPropertyKey.has(key)) {
1084
+ materializedChildren ??= {
1085
+ ...prevData
1086
+ };
1087
+ materializedChildren.byPropertyKey.delete(key);
998
1088
  }
999
- for (const key of Object.keys(value)) {
1000
- let trackingKey = undefined;
1001
- const childValue = value[key];
1002
- if (childValue === undefined) {
1003
- if (prevData.byPropertyKey.has(key)) {
1004
- data ??= {
1005
- ...prevData
1006
- };
1007
- data.byPropertyKey.delete(key);
1008
- }
1009
- continue;
1010
- }
1011
- if (parentIsArray && isObject(childValue) && !isArray(childValue)) {
1012
- trackingKey = childValue[this.identitySymbol] ??= Symbol(ngDevMode ? `id:${globalId++}` : '');
1013
- }
1014
- let childNode;
1015
- if (trackingKey) {
1016
- if (!prevData.byTrackingKey?.has(trackingKey)) {
1017
- data ??= {
1018
- ...prevData
1019
- };
1020
- data.byTrackingKey ??= new Map();
1021
- data.byTrackingKey.set(trackingKey, this.createChildNode(key, trackingKey, parentIsArray));
1022
- }
1023
- childNode = (data ?? prevData).byTrackingKey.get(trackingKey);
1024
- }
1025
- const child = prevData.byPropertyKey.get(key);
1026
- if (child === undefined) {
1027
- data ??= {
1028
- ...prevData
1029
- };
1030
- data.byPropertyKey.set(key, {
1031
- reader: this.createReader(key),
1032
- node: childNode ?? this.createChildNode(key, trackingKey, parentIsArray)
1033
- });
1034
- } else if (childNode && childNode !== child.node) {
1035
- data ??= {
1036
- ...prevData
1037
- };
1038
- child.node = childNode;
1039
- }
1089
+ continue;
1090
+ }
1091
+ if (parentIsArray && isObject(childValue) && !isArray(childValue)) {
1092
+ trackingKey = childValue[this.identitySymbol] ??= Symbol(ngDevMode ? `id:${globalId++}` : '');
1093
+ }
1094
+ let childNode;
1095
+ if (trackingKey) {
1096
+ if (!prevData.byTrackingKey?.has(trackingKey)) {
1097
+ materializedChildren ??= {
1098
+ ...prevData
1099
+ };
1100
+ materializedChildren.byTrackingKey ??= new Map();
1101
+ materializedChildren.byTrackingKey.set(trackingKey, this.createChildNode(key, trackingKey, parentIsArray));
1040
1102
  }
1041
- return data ?? prevData;
1103
+ childNode = (materializedChildren ?? prevData).byTrackingKey.get(trackingKey);
1042
1104
  }
1043
- });
1105
+ const child = prevData.byPropertyKey.get(key);
1106
+ if (child === undefined) {
1107
+ materializedChildren ??= {
1108
+ ...prevData
1109
+ };
1110
+ materializedChildren.byPropertyKey.set(key, {
1111
+ reader: this.createReader(key),
1112
+ node: childNode ?? this.createChildNode(key, trackingKey, parentIsArray)
1113
+ });
1114
+ } else if (childNode && childNode !== child.node) {
1115
+ materializedChildren ??= {
1116
+ ...prevData
1117
+ };
1118
+ child.node = childNode;
1119
+ }
1120
+ }
1121
+ return materializedChildren ?? prevData;
1044
1122
  }
1045
1123
  createReader(key) {
1046
1124
  return computed(() => this.childrenMap()?.byPropertyKey.get(key)?.node);
@@ -1202,6 +1280,7 @@ class FieldNode {
1202
1280
  this.metadataState = new FieldMetadataState(this);
1203
1281
  this.submitState = new FieldSubmitState(this);
1204
1282
  this.controlValue = this.controlValueSignal();
1283
+ this.metadataState.runMetadataCreateLifecycle();
1205
1284
  }
1206
1285
  focusBoundControl(options) {
1207
1286
  this.getBindingForFocus()?.focus(options);
@@ -1299,15 +1378,30 @@ class FieldNode {
1299
1378
  metadata(key) {
1300
1379
  return this.metadataState.get(key);
1301
1380
  }
1381
+ getError(kind) {
1382
+ return this.errors().find(e => e.kind === kind);
1383
+ }
1302
1384
  hasMetadata(key) {
1303
1385
  return this.metadataState.has(key);
1304
1386
  }
1305
- markAsTouched() {
1387
+ markAsTouched(options) {
1306
1388
  untracked(() => {
1307
- this.nodeState.markAsTouched();
1389
+ this.markAsTouchedInternal(options);
1308
1390
  this.flushSync();
1309
1391
  });
1310
1392
  }
1393
+ markAsTouchedInternal(options) {
1394
+ if (this.validationState.shouldSkipValidation()) {
1395
+ return;
1396
+ }
1397
+ this.nodeState.markAsTouched();
1398
+ if (options?.skipDescendants) {
1399
+ return;
1400
+ }
1401
+ for (const child of this.structure.children()) {
1402
+ child.markAsTouchedInternal();
1403
+ }
1404
+ }
1311
1405
  markAsDirty() {
1312
1406
  this.nodeState.markAsDirty();
1313
1407
  }
@@ -1330,6 +1424,21 @@ class FieldNode {
1330
1424
  child._reset();
1331
1425
  }
1332
1426
  }
1427
+ reloadValidation() {
1428
+ untracked(() => this._reloadValidation());
1429
+ }
1430
+ _reloadValidation() {
1431
+ const keys = this.logicNode.logic.getMetadataKeys();
1432
+ for (const key of keys) {
1433
+ if (key[IS_ASYNC_VALIDATION_RESOURCE]) {
1434
+ const resource = this.metadata(key);
1435
+ resource.reload?.();
1436
+ }
1437
+ }
1438
+ for (const child of this.structure.children()) {
1439
+ child._reloadValidation();
1440
+ }
1441
+ }
1333
1442
  controlValueSignal() {
1334
1443
  const controlValue = linkedSignal(this.value, ...(ngDevMode ? [{
1335
1444
  debugName: "controlValue"
@@ -1617,19 +1726,11 @@ async function submit(form, options) {
1617
1726
  if (!action) {
1618
1727
  throw new _RuntimeError(1915, (typeof ngDevMode === 'undefined' || ngDevMode) && 'Cannot submit form with no submit action. Specify the action when creating the form, or as an additional argument to `submit()`.');
1619
1728
  }
1729
+ node.markAsTouched();
1620
1730
  const onInvalid = options?.onInvalid;
1621
- const ignoreValidators = options?.ignoreValidators ?? 'pending';
1622
- let shouldRunAction = true;
1623
- untracked(() => {
1624
- markAllAsTouched(node);
1625
- if (ignoreValidators === 'none') {
1626
- shouldRunAction = node.valid();
1627
- } else if (ignoreValidators === 'pending') {
1628
- shouldRunAction = !node.invalid();
1629
- }
1630
- });
1731
+ const shouldRun = shouldRunAction(node, options?.ignoreValidators);
1631
1732
  try {
1632
- if (shouldRunAction) {
1733
+ if (shouldRun) {
1633
1734
  node.submitState.selfSubmitting.set(true);
1634
1735
  const errors = await untracked(() => action?.(field, detail));
1635
1736
  errors && setSubmissionErrors(node, errors);
@@ -1645,13 +1746,14 @@ async function submit(form, options) {
1645
1746
  function schema(fn) {
1646
1747
  return SchemaImpl.create(fn);
1647
1748
  }
1648
- function markAllAsTouched(node) {
1649
- if (node.validationState.shouldSkipValidation()) {
1650
- return;
1651
- }
1652
- node.markAsTouched();
1653
- for (const child of node.structure.children()) {
1654
- markAllAsTouched(child);
1749
+ function shouldRunAction(node, ignoreValidators) {
1750
+ switch (ignoreValidators) {
1751
+ case 'all':
1752
+ return true;
1753
+ case 'none':
1754
+ return untracked(node.valid);
1755
+ default:
1756
+ return !untracked(node.invalid);
1655
1757
  }
1656
1758
  }
1657
1759
  function setSubmissionErrors(submittedField, errors) {
@@ -1725,5 +1827,5 @@ function extractNestedReactiveErrors(control) {
1725
1827
  return errors;
1726
1828
  }
1727
1829
 
1728
- 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 };
1830
+ export { BasicFieldAdapter, CompatValidationError, DEBOUNCER, FieldNode, FieldNodeState, FieldNodeStructure, FieldPathNode, IS_ASYNC_VALIDATION_RESOURCE, MAX, MAX_LENGTH, MIN, MIN_LENGTH, MetadataKey, MetadataReducer, PATTERN, REQUIRED, addDefaultField, apply, applyEach, applyWhen, applyWhenValue, assertPathIsCurrent, calculateValidationSelfStatus, createManagedMetadataKey, createMetadataKey, extractNestedReactiveErrors, form, getInjectorFromOptions, isArray, isObject, metadata, normalizeFormArgs, schema, signalErrorsToValidationErrors, submit };
1729
1831
  //# sourceMappingURL=_validation_errors-chunk.mjs.map