@angular/forms 22.0.0-next.4 → 22.0.0-next.6
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/_validation_errors-chunk.mjs +158 -67
- package/fesm2022/_validation_errors-chunk.mjs.map +1 -1
- package/fesm2022/forms.mjs +129 -129
- package/fesm2022/forms.mjs.map +1 -1
- package/fesm2022/signals-compat.mjs +1 -1
- package/fesm2022/signals.mjs +177 -17
- 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 +33 -11
- package/types/forms.d.ts +1 -1
- package/types/signals-compat.d.ts +1 -1
- package/types/signals.d.ts +14 -4
package/fesm2022/signals.mjs
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Angular v22.0.0-next.
|
|
2
|
+
* @license Angular v22.0.0-next.6
|
|
3
3
|
* (c) 2010-2026 Google LLC. https://angular.dev/
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import * as i0 from '@angular/core';
|
|
8
|
-
import { InjectionToken, resource, ɵisPromise as _isPromise, linkedSignal, inject, ɵRuntimeError as _RuntimeError, untracked, input, computed, Renderer2, DestroyRef, Injector, ElementRef, signal, afterRenderEffect, effect, ɵformatRuntimeError as _formatRuntimeError, Directive } from '@angular/core';
|
|
8
|
+
import { InjectionToken, debounced, resource, ɵisPromise as _isPromise, linkedSignal, inject, ɵRuntimeError as _RuntimeError, untracked, CSP_NONCE, PLATFORM_ID, Injectable, forwardRef, input, computed, Renderer2, DestroyRef, Injector, ElementRef, signal, afterRenderEffect, effect, ɵformatRuntimeError as _formatRuntimeError, Directive } from '@angular/core';
|
|
9
9
|
import { ɵFORM_FIELD_PARSE_ERRORS as _FORM_FIELD_PARSE_ERRORS, Validators, ɵsetNativeDomProperty as _setNativeDomProperty, ɵisNativeFormElement as _isNativeFormElement, ɵisNumericFormElement as _isNumericFormElement, ɵisTextualFormElement as _isTextualFormElement, NG_VALUE_ACCESSOR, NgControl } from '@angular/forms';
|
|
10
|
-
import { assertPathIsCurrent, FieldPathNode, addDefaultField, metadata, createMetadataKey, MAX, MAX_LENGTH, MIN, MIN_LENGTH, PATTERN, REQUIRED, createManagedMetadataKey, DEBOUNCER, signalErrorsToValidationErrors, submit } from './_validation_errors-chunk.mjs';
|
|
10
|
+
import { assertPathIsCurrent, FieldPathNode, addDefaultField, metadata, createMetadataKey, MAX, MAX_LENGTH, MIN, MIN_LENGTH, PATTERN, REQUIRED, createManagedMetadataKey, IS_ASYNC_VALIDATION_RESOURCE, DEBOUNCER, signalErrorsToValidationErrors, submit } from './_validation_errors-chunk.mjs';
|
|
11
11
|
export { MetadataKey, MetadataReducer, apply, applyEach, applyWhen, applyWhenValue, form, schema } from './_validation_errors-chunk.mjs';
|
|
12
|
+
import { DOCUMENT, isPlatformBrowser } from '@angular/common';
|
|
12
13
|
import { httpResource } from '@angular/common/http';
|
|
13
14
|
import '@angular/core/primitives/signals';
|
|
14
15
|
|
|
@@ -344,7 +345,14 @@ function required(path, config) {
|
|
|
344
345
|
function validateAsync(path, opts) {
|
|
345
346
|
assertPathIsCurrent(path);
|
|
346
347
|
const pathNode = FieldPathNode.unwrapFieldPath(path);
|
|
347
|
-
const RESOURCE = createManagedMetadataKey(
|
|
348
|
+
const RESOURCE = createManagedMetadataKey((_state, params) => {
|
|
349
|
+
if (opts.debounce !== undefined) {
|
|
350
|
+
const debouncedResource = debounced(() => params(), opts.debounce);
|
|
351
|
+
return opts.factory(debouncedResource.value);
|
|
352
|
+
}
|
|
353
|
+
return opts.factory(params);
|
|
354
|
+
});
|
|
355
|
+
RESOURCE[IS_ASYNC_VALIDATION_RESOURCE] = true;
|
|
348
356
|
metadata(path, RESOURCE, ctx => {
|
|
349
357
|
const node = ctx.stateOf(path);
|
|
350
358
|
const validationState = node.validationState;
|
|
@@ -445,6 +453,7 @@ class StandardSchemaValidationError extends BaseNgValidationError {
|
|
|
445
453
|
function validateHttp(path, opts) {
|
|
446
454
|
validateAsync(path, {
|
|
447
455
|
params: opts.request,
|
|
456
|
+
debounce: opts.debounce,
|
|
448
457
|
factory: request => httpResource(request, opts.options),
|
|
449
458
|
onSuccess: opts.onSuccess,
|
|
450
459
|
onError: opts.onError
|
|
@@ -654,9 +663,9 @@ function bindingUpdated(bindings, key, value) {
|
|
|
654
663
|
return false;
|
|
655
664
|
}
|
|
656
665
|
|
|
657
|
-
function getNativeControlValue(element, currentValue) {
|
|
666
|
+
function getNativeControlValue(element, currentValue, validityMonitor) {
|
|
658
667
|
let modelValue;
|
|
659
|
-
if (element.
|
|
668
|
+
if (isInput(element) && validityMonitor.isBadInput(element)) {
|
|
660
669
|
return {
|
|
661
670
|
error: new NativeInputParseError()
|
|
662
671
|
};
|
|
@@ -692,6 +701,25 @@ function getNativeControlValue(element, currentValue) {
|
|
|
692
701
|
}
|
|
693
702
|
break;
|
|
694
703
|
}
|
|
704
|
+
if (element.tagName === 'INPUT' && element.type === 'text') {
|
|
705
|
+
modelValue ??= untracked(currentValue);
|
|
706
|
+
if (typeof modelValue === 'number' || modelValue === null) {
|
|
707
|
+
if (element.value === '') {
|
|
708
|
+
return {
|
|
709
|
+
value: null
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
const parsed = Number(element.value);
|
|
713
|
+
if (Number.isNaN(parsed)) {
|
|
714
|
+
return {
|
|
715
|
+
error: new NativeInputParseError()
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
return {
|
|
719
|
+
value: parsed
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
}
|
|
695
723
|
return {
|
|
696
724
|
value: element.value
|
|
697
725
|
};
|
|
@@ -727,6 +755,16 @@ function setNativeControlValue(element, value) {
|
|
|
727
755
|
return;
|
|
728
756
|
}
|
|
729
757
|
}
|
|
758
|
+
if (element.tagName === 'INPUT' && element.type === 'text') {
|
|
759
|
+
if (typeof value === 'number') {
|
|
760
|
+
element.value = isNaN(value) ? '' : String(value);
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
if (value === null) {
|
|
764
|
+
element.value = '';
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
730
768
|
element.value = value;
|
|
731
769
|
}
|
|
732
770
|
function setNativeNumberControlValue(element, value) {
|
|
@@ -736,6 +774,12 @@ function setNativeNumberControlValue(element, value) {
|
|
|
736
774
|
element.valueAsNumber = value;
|
|
737
775
|
}
|
|
738
776
|
}
|
|
777
|
+
function isInput(element) {
|
|
778
|
+
return element.tagName === 'INPUT';
|
|
779
|
+
}
|
|
780
|
+
function inputRequiresValidityTracking(input) {
|
|
781
|
+
return input.type === 'date' || input.type === 'datetime-local' || input.type === 'month' || input.type === 'time' || input.type === 'week';
|
|
782
|
+
}
|
|
739
783
|
|
|
740
784
|
function customControlCreate(host, parent) {
|
|
741
785
|
host.listenToCustomControlModel(value => parent.state().controlValue.set(value));
|
|
@@ -831,13 +875,16 @@ function isRelevantSelectMutation(mutation) {
|
|
|
831
875
|
return false;
|
|
832
876
|
}
|
|
833
877
|
|
|
834
|
-
function nativeControlCreate(host, parent, parseErrorsSource) {
|
|
878
|
+
function nativeControlCreate(host, parent, parseErrorsSource, validityMonitor) {
|
|
835
879
|
let updateMode = false;
|
|
836
880
|
const input = parent.nativeFormElement;
|
|
837
|
-
const parser = createParser(() => parent.state().value(), rawValue => parent.state().controlValue.set(rawValue),
|
|
881
|
+
const parser = createParser(() => parent.state().value(), rawValue => parent.state().controlValue.set(rawValue), _rawValue => getNativeControlValue(input, parent.state().value, validityMonitor));
|
|
838
882
|
parseErrorsSource.set(parser.errors);
|
|
839
883
|
host.listenToDom('input', () => parser.setRawValue(undefined));
|
|
840
884
|
host.listenToDom('blur', () => parent.state().markAsTouched());
|
|
885
|
+
if (isInput(input) && inputRequiresValidityTracking(input)) {
|
|
886
|
+
validityMonitor.watchValidity(input, () => parser.setRawValue(undefined));
|
|
887
|
+
}
|
|
841
888
|
parent.registerAsBinding();
|
|
842
889
|
if (input.tagName === 'SELECT') {
|
|
843
890
|
observeSelectMutations(input, () => {
|
|
@@ -867,6 +914,112 @@ function nativeControlCreate(host, parent, parseErrorsSource) {
|
|
|
867
914
|
};
|
|
868
915
|
}
|
|
869
916
|
|
|
917
|
+
class InputValidityMonitor {
|
|
918
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
919
|
+
minVersion: "12.0.0",
|
|
920
|
+
version: "22.0.0-next.6",
|
|
921
|
+
ngImport: i0,
|
|
922
|
+
type: InputValidityMonitor,
|
|
923
|
+
deps: [],
|
|
924
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
925
|
+
});
|
|
926
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
927
|
+
minVersion: "12.0.0",
|
|
928
|
+
version: "22.0.0-next.6",
|
|
929
|
+
ngImport: i0,
|
|
930
|
+
type: InputValidityMonitor,
|
|
931
|
+
providedIn: 'root',
|
|
932
|
+
useClass: i0.forwardRef(() => AnimationInputValidityMonitor)
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
936
|
+
minVersion: "12.0.0",
|
|
937
|
+
version: "22.0.0-next.6",
|
|
938
|
+
ngImport: i0,
|
|
939
|
+
type: InputValidityMonitor,
|
|
940
|
+
decorators: [{
|
|
941
|
+
type: Injectable,
|
|
942
|
+
args: [{
|
|
943
|
+
providedIn: 'root',
|
|
944
|
+
useClass: forwardRef(() => AnimationInputValidityMonitor)
|
|
945
|
+
}]
|
|
946
|
+
}]
|
|
947
|
+
});
|
|
948
|
+
class AnimationInputValidityMonitor extends InputValidityMonitor {
|
|
949
|
+
document = inject(DOCUMENT);
|
|
950
|
+
cspNonce = inject(CSP_NONCE, {
|
|
951
|
+
optional: true
|
|
952
|
+
});
|
|
953
|
+
isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
954
|
+
injectedStyles = new WeakMap();
|
|
955
|
+
watchValidity(element, callback) {
|
|
956
|
+
if (!this.isBrowser) {
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
const rootNode = element.getRootNode();
|
|
960
|
+
if (!this.injectedStyles.has(rootNode)) {
|
|
961
|
+
this.injectedStyles.set(rootNode, this.createTransitionStyle(rootNode));
|
|
962
|
+
}
|
|
963
|
+
element.addEventListener('animationstart', event => {
|
|
964
|
+
const animationEvent = event;
|
|
965
|
+
if (animationEvent.animationName === 'ng-valid' || animationEvent.animationName === 'ng-invalid') {
|
|
966
|
+
callback();
|
|
967
|
+
}
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
isBadInput(element) {
|
|
971
|
+
return element.validity?.badInput ?? false;
|
|
972
|
+
}
|
|
973
|
+
createTransitionStyle(rootNode) {
|
|
974
|
+
const element = this.document.createElement('style');
|
|
975
|
+
if (this.cspNonce) {
|
|
976
|
+
element.nonce = this.cspNonce;
|
|
977
|
+
}
|
|
978
|
+
element.textContent = `
|
|
979
|
+
@keyframes ng-valid {}
|
|
980
|
+
@keyframes ng-invalid {}
|
|
981
|
+
input:valid, textarea:valid {
|
|
982
|
+
animation: ng-valid 0.001s;
|
|
983
|
+
}
|
|
984
|
+
input:invalid, textarea:invalid {
|
|
985
|
+
animation: ng-invalid 0.001s;
|
|
986
|
+
}
|
|
987
|
+
`;
|
|
988
|
+
if (rootNode.nodeType === 9) {
|
|
989
|
+
rootNode.head?.appendChild(element);
|
|
990
|
+
} else {
|
|
991
|
+
rootNode.appendChild(element);
|
|
992
|
+
}
|
|
993
|
+
return element;
|
|
994
|
+
}
|
|
995
|
+
ngOnDestroy() {
|
|
996
|
+
this.injectedStyles.get(this.document)?.remove();
|
|
997
|
+
}
|
|
998
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
999
|
+
minVersion: "12.0.0",
|
|
1000
|
+
version: "22.0.0-next.6",
|
|
1001
|
+
ngImport: i0,
|
|
1002
|
+
type: AnimationInputValidityMonitor,
|
|
1003
|
+
deps: null,
|
|
1004
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
1005
|
+
});
|
|
1006
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
1007
|
+
minVersion: "12.0.0",
|
|
1008
|
+
version: "22.0.0-next.6",
|
|
1009
|
+
ngImport: i0,
|
|
1010
|
+
type: AnimationInputValidityMonitor
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
1014
|
+
minVersion: "12.0.0",
|
|
1015
|
+
version: "22.0.0-next.6",
|
|
1016
|
+
ngImport: i0,
|
|
1017
|
+
type: AnimationInputValidityMonitor,
|
|
1018
|
+
decorators: [{
|
|
1019
|
+
type: Injectable
|
|
1020
|
+
}]
|
|
1021
|
+
});
|
|
1022
|
+
|
|
870
1023
|
const ɵNgFieldDirective = Symbol();
|
|
871
1024
|
const FORM_FIELD = new InjectionToken(typeof ngDevMode !== 'undefined' && ngDevMode ? 'FORM_FIELD' : '');
|
|
872
1025
|
class FormField {
|
|
@@ -895,6 +1048,7 @@ class FormField {
|
|
|
895
1048
|
config = inject(SIGNAL_FORMS_CONFIG, {
|
|
896
1049
|
optional: true
|
|
897
1050
|
});
|
|
1051
|
+
validityMonitor = inject(InputValidityMonitor);
|
|
898
1052
|
parseErrorsSource = signal(undefined, ...(ngDevMode ? [{
|
|
899
1053
|
debugName: "parseErrorsSource"
|
|
900
1054
|
}] : []));
|
|
@@ -982,7 +1136,7 @@ class FormField {
|
|
|
982
1136
|
} else if (host.customControl) {
|
|
983
1137
|
this.ɵngControlUpdate = customControlCreate(host, this);
|
|
984
1138
|
} else if (this.elementIsNativeFormElement) {
|
|
985
|
-
this.ɵngControlUpdate = nativeControlCreate(host, this, this.parseErrorsSource);
|
|
1139
|
+
this.ɵngControlUpdate = nativeControlCreate(host, this, this.parseErrorsSource, this.validityMonitor);
|
|
986
1140
|
} else {
|
|
987
1141
|
throw new _RuntimeError(1914, typeof ngDevMode !== 'undefined' && ngDevMode && `${host.descriptor} is an invalid [formField] directive host. The host must be a native form control ` + `(such as <input>', '<select>', or '<textarea>') or a custom form control with a 'value' or ` + `'checked' model.`);
|
|
988
1142
|
}
|
|
@@ -1010,7 +1164,7 @@ class FormField {
|
|
|
1010
1164
|
}
|
|
1011
1165
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
1012
1166
|
minVersion: "12.0.0",
|
|
1013
|
-
version: "22.0.0-next.
|
|
1167
|
+
version: "22.0.0-next.6",
|
|
1014
1168
|
ngImport: i0,
|
|
1015
1169
|
type: FormField,
|
|
1016
1170
|
deps: [],
|
|
@@ -1018,7 +1172,7 @@ class FormField {
|
|
|
1018
1172
|
});
|
|
1019
1173
|
static ɵdir = i0.ɵɵngDeclareDirective({
|
|
1020
1174
|
minVersion: "17.1.0",
|
|
1021
|
-
version: "22.0.0-next.
|
|
1175
|
+
version: "22.0.0-next.6",
|
|
1022
1176
|
type: FormField,
|
|
1023
1177
|
isStandalone: true,
|
|
1024
1178
|
selector: "[formField]",
|
|
@@ -1050,7 +1204,7 @@ class FormField {
|
|
|
1050
1204
|
}
|
|
1051
1205
|
i0.ɵɵngDeclareClassMetadata({
|
|
1052
1206
|
minVersion: "12.0.0",
|
|
1053
|
-
version: "22.0.0-next.
|
|
1207
|
+
version: "22.0.0-next.6",
|
|
1054
1208
|
ngImport: i0,
|
|
1055
1209
|
type: FormField,
|
|
1056
1210
|
decorators: [{
|
|
@@ -1091,11 +1245,17 @@ class FormRoot {
|
|
|
1091
1245
|
});
|
|
1092
1246
|
onSubmit(event) {
|
|
1093
1247
|
event.preventDefault();
|
|
1094
|
-
|
|
1248
|
+
untracked(() => {
|
|
1249
|
+
const fieldTree = this.fieldTree();
|
|
1250
|
+
const node = fieldTree();
|
|
1251
|
+
if (node.structure.fieldManager.submitOptions) {
|
|
1252
|
+
submit(fieldTree);
|
|
1253
|
+
}
|
|
1254
|
+
});
|
|
1095
1255
|
}
|
|
1096
1256
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
1097
1257
|
minVersion: "12.0.0",
|
|
1098
|
-
version: "22.0.0-next.
|
|
1258
|
+
version: "22.0.0-next.6",
|
|
1099
1259
|
ngImport: i0,
|
|
1100
1260
|
type: FormRoot,
|
|
1101
1261
|
deps: [],
|
|
@@ -1103,7 +1263,7 @@ class FormRoot {
|
|
|
1103
1263
|
});
|
|
1104
1264
|
static ɵdir = i0.ɵɵngDeclareDirective({
|
|
1105
1265
|
minVersion: "17.1.0",
|
|
1106
|
-
version: "22.0.0-next.
|
|
1266
|
+
version: "22.0.0-next.6",
|
|
1107
1267
|
type: FormRoot,
|
|
1108
1268
|
isStandalone: true,
|
|
1109
1269
|
selector: "form[formRoot]",
|
|
@@ -1129,7 +1289,7 @@ class FormRoot {
|
|
|
1129
1289
|
}
|
|
1130
1290
|
i0.ɵɵngDeclareClassMetadata({
|
|
1131
1291
|
minVersion: "12.0.0",
|
|
1132
|
-
version: "22.0.0-next.
|
|
1292
|
+
version: "22.0.0-next.6",
|
|
1133
1293
|
ngImport: i0,
|
|
1134
1294
|
type: FormRoot,
|
|
1135
1295
|
decorators: [{
|
|
@@ -1154,5 +1314,5 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
1154
1314
|
}
|
|
1155
1315
|
});
|
|
1156
1316
|
|
|
1157
|
-
export { BaseNgValidationError, EmailValidationError, FORM_FIELD, FormField, FormRoot, MAX, MAX_LENGTH, MIN, MIN_LENGTH, MaxLengthValidationError, MaxValidationError, MinLengthValidationError, MinValidationError, NativeInputParseError, NgValidationError, PATTERN, PatternValidationError, REQUIRED, RequiredValidationError, StandardSchemaValidationError, createManagedMetadataKey, createMetadataKey, debounce, disabled, email, emailError, hidden, max, maxError, maxLength, maxLengthError, metadata, min, minError, minLength, minLengthError, pattern, patternError, provideSignalFormsConfig, readonly, required, requiredError, standardSchemaError, submit, transformedValue, validate, validateAsync, validateHttp, validateStandardSchema, validateTree, ɵNgFieldDirective };
|
|
1317
|
+
export { BaseNgValidationError, EmailValidationError, FORM_FIELD, FormField, FormRoot, IS_ASYNC_VALIDATION_RESOURCE, MAX, MAX_LENGTH, MIN, MIN_LENGTH, MaxLengthValidationError, MaxValidationError, MinLengthValidationError, MinValidationError, NativeInputParseError, NgValidationError, PATTERN, PatternValidationError, REQUIRED, RequiredValidationError, StandardSchemaValidationError, createManagedMetadataKey, createMetadataKey, debounce, disabled, email, emailError, hidden, max, maxError, maxLength, maxLengthError, metadata, min, minError, minLength, minLengthError, pattern, patternError, provideSignalFormsConfig, readonly, required, requiredError, standardSchemaError, submit, transformedValue, validate, validateAsync, validateHttp, validateStandardSchema, validateTree, ɵNgFieldDirective };
|
|
1158
1318
|
//# sourceMappingURL=signals.mjs.map
|