@angular/forms 21.2.0-next.2 → 21.2.0-next.3

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,15 +1,15 @@
1
1
  /**
2
- * @license Angular v21.2.0-next.2
2
+ * @license Angular v21.2.0-next.3
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, ɵRuntimeError as _RuntimeError, untracked, input, inject, Renderer2, DestroyRef, computed, Injector, ElementRef, signal, afterRenderEffect, effect, Directive, ɵisPromise as _isPromise, resource } from '@angular/core';
9
- import { Validators, NG_VALUE_ACCESSOR, NgControl } from '@angular/forms';
10
- import { signalErrorsToValidationErrors, assertPathIsCurrent, FieldPathNode, addDefaultField, metadata, createMetadataKey, MAX, MAX_LENGTH, MIN, MIN_LENGTH, PATTERN, REQUIRED, createManagedMetadataKey, DEBOUNCER } from './_structure-chunk.mjs';
11
- export { MetadataKey, MetadataReducer, apply, applyEach, applyWhen, applyWhenValue, form, schema, submit } from './_structure-chunk.mjs';
8
+ import { InjectionToken, ɵisPromise as _isPromise, resource, signal, linkedSignal, inject, ɵRuntimeError as _RuntimeError, untracked, input, Renderer2, DestroyRef, computed, Injector, ElementRef, afterRenderEffect, effect, Directive } from '@angular/core';
9
+ import { assertPathIsCurrent, FieldPathNode, addDefaultField, metadata, createMetadataKey, MAX, MAX_LENGTH, MIN, MIN_LENGTH, PATTERN, REQUIRED, createManagedMetadataKey, DEBOUNCER, signalErrorsToValidationErrors, submit } from './_validation_errors-chunk.mjs';
10
+ export { MetadataKey, MetadataReducer, apply, applyEach, applyWhen, applyWhenValue, form, schema } from './_validation_errors-chunk.mjs';
12
11
  import { httpResource } from '@angular/common/http';
12
+ import { Validators, NG_VALUE_ACCESSOR, NgControl } from '@angular/forms';
13
13
  import '@angular/core/primitives/signals';
14
14
 
15
15
  const SIGNAL_FORMS_CONFIG = new InjectionToken(typeof ngDevMode !== 'undefined' && ngDevMode ? 'SIGNAL_FORMS_CONFIG' : '');
@@ -21,989 +21,1100 @@ function provideSignalFormsConfig(config) {
21
21
  }];
22
22
  }
23
23
 
24
- class InteropNgControl {
25
- field;
26
- constructor(field) {
27
- this.field = field;
28
- }
29
- control = this;
30
- get value() {
31
- return this.field().value();
32
- }
33
- get valid() {
34
- return this.field().valid();
35
- }
36
- get invalid() {
37
- return this.field().invalid();
38
- }
39
- get pending() {
40
- return this.field().pending();
41
- }
42
- get disabled() {
43
- return this.field().disabled();
44
- }
45
- get enabled() {
46
- return !this.field().disabled();
47
- }
48
- get errors() {
49
- return signalErrorsToValidationErrors(this.field().errors());
50
- }
51
- get pristine() {
52
- return !this.field().dirty();
53
- }
54
- get dirty() {
55
- return this.field().dirty();
56
- }
57
- get touched() {
58
- return this.field().touched();
59
- }
60
- get untouched() {
61
- return !this.field().touched();
62
- }
63
- get status() {
64
- if (this.field().disabled()) {
65
- return 'DISABLED';
66
- }
67
- if (this.field().valid()) {
68
- return 'VALID';
69
- }
70
- if (this.field().invalid()) {
71
- return 'INVALID';
72
- }
73
- if (this.field().pending()) {
74
- return 'PENDING';
24
+ function disabled(path, logic) {
25
+ assertPathIsCurrent(path);
26
+ const pathNode = FieldPathNode.unwrapFieldPath(path);
27
+ pathNode.builder.addDisabledReasonRule(ctx => {
28
+ let result = true;
29
+ if (typeof logic === 'string') {
30
+ result = logic;
31
+ } else if (logic) {
32
+ result = logic(ctx);
75
33
  }
76
- throw new _RuntimeError(1910, ngDevMode && 'Unknown form control status');
77
- }
78
- valueAccessor = null;
79
- hasValidator(validator) {
80
- if (validator === Validators.required) {
81
- return this.field().required();
34
+ if (typeof result === 'string') {
35
+ return {
36
+ fieldTree: ctx.fieldTree,
37
+ message: result
38
+ };
82
39
  }
83
- return false;
40
+ return result ? {
41
+ fieldTree: ctx.fieldTree
42
+ } : undefined;
43
+ });
44
+ }
45
+
46
+ function hidden(path, logic) {
47
+ assertPathIsCurrent(path);
48
+ const pathNode = FieldPathNode.unwrapFieldPath(path);
49
+ pathNode.builder.addHiddenRule(logic);
50
+ }
51
+
52
+ function readonly(path, logic = () => true) {
53
+ assertPathIsCurrent(path);
54
+ const pathNode = FieldPathNode.unwrapFieldPath(path);
55
+ pathNode.builder.addReadonlyRule(logic);
56
+ }
57
+
58
+ function getLengthOrSize(value) {
59
+ const v = value;
60
+ return typeof v.length === 'number' ? v.length : v.size;
61
+ }
62
+ function getOption(opt, ctx) {
63
+ return opt instanceof Function ? opt(ctx) : opt;
64
+ }
65
+ function isEmpty(value) {
66
+ if (typeof value === 'number') {
67
+ return isNaN(value);
84
68
  }
85
- updateValueAndValidity() {}
69
+ return value === '' || value === false || value == null;
86
70
  }
87
71
 
88
- function isNativeFormElement(element) {
89
- return element.tagName === 'INPUT' || element.tagName === 'SELECT' || element.tagName === 'TEXTAREA';
72
+ function validate(path, logic) {
73
+ assertPathIsCurrent(path);
74
+ const pathNode = FieldPathNode.unwrapFieldPath(path);
75
+ pathNode.builder.addSyncErrorRule(ctx => {
76
+ return addDefaultField(logic(ctx), ctx.fieldTree);
77
+ });
90
78
  }
91
- function isNumericFormElement(element) {
92
- if (element.tagName !== 'INPUT') {
93
- return false;
79
+
80
+ function requiredError(options) {
81
+ return new RequiredValidationError(options);
82
+ }
83
+ function minError(min, options) {
84
+ return new MinValidationError(min, options);
85
+ }
86
+ function maxError(max, options) {
87
+ return new MaxValidationError(max, options);
88
+ }
89
+ function minLengthError(minLength, options) {
90
+ return new MinLengthValidationError(minLength, options);
91
+ }
92
+ function maxLengthError(maxLength, options) {
93
+ return new MaxLengthValidationError(maxLength, options);
94
+ }
95
+ function patternError(pattern, options) {
96
+ return new PatternValidationError(pattern, options);
97
+ }
98
+ function emailError(options) {
99
+ return new EmailValidationError(options);
100
+ }
101
+ class BaseNgValidationError {
102
+ __brand = undefined;
103
+ kind = '';
104
+ fieldTree;
105
+ message;
106
+ constructor(options) {
107
+ if (options) {
108
+ Object.assign(this, options);
109
+ }
94
110
  }
95
- const type = element.type;
96
- return type === 'date' || type === 'datetime-local' || type === 'month' || type === 'number' || type === 'range' || type === 'time' || type === 'week';
97
111
  }
98
- function isTextualFormElement(element) {
99
- return element.tagName === 'INPUT' || element.tagName === 'TEXTAREA';
112
+ class RequiredValidationError extends BaseNgValidationError {
113
+ kind = 'required';
100
114
  }
101
- function getNativeControlValue(element, currentValue) {
102
- switch (element.type) {
103
- case 'checkbox':
104
- return element.checked;
105
- case 'number':
106
- case 'range':
107
- case 'datetime-local':
108
- if (typeof untracked(currentValue) === 'number') {
109
- return element.valueAsNumber;
110
- }
111
- break;
112
- case 'date':
113
- case 'month':
114
- case 'time':
115
- case 'week':
116
- const value = untracked(currentValue);
117
- if (value === null || value instanceof Date) {
118
- return element.valueAsDate;
119
- } else if (typeof value === 'number') {
120
- return element.valueAsNumber;
121
- }
122
- break;
115
+ class MinValidationError extends BaseNgValidationError {
116
+ min;
117
+ kind = 'min';
118
+ constructor(min, options) {
119
+ super(options);
120
+ this.min = min;
123
121
  }
124
- return element.value;
125
122
  }
126
- function setNativeControlValue(element, value) {
127
- switch (element.type) {
128
- case 'checkbox':
129
- element.checked = value;
130
- return;
131
- case 'radio':
132
- element.checked = value === element.value;
133
- return;
134
- case 'number':
135
- case 'range':
136
- case 'datetime-local':
137
- if (typeof value === 'number') {
138
- setNativeNumberControlValue(element, value);
139
- return;
140
- }
141
- break;
142
- case 'date':
143
- case 'month':
144
- case 'time':
145
- case 'week':
146
- if (value === null || value instanceof Date) {
147
- element.valueAsDate = value;
148
- return;
149
- } else if (typeof value === 'number') {
150
- setNativeNumberControlValue(element, value);
151
- return;
152
- }
123
+ class MaxValidationError extends BaseNgValidationError {
124
+ max;
125
+ kind = 'max';
126
+ constructor(max, options) {
127
+ super(options);
128
+ this.max = max;
153
129
  }
154
- element.value = value;
155
130
  }
156
- function setNativeNumberControlValue(element, value) {
157
- if (isNaN(value)) {
158
- element.value = '';
159
- } else {
160
- element.valueAsNumber = value;
131
+ class MinLengthValidationError extends BaseNgValidationError {
132
+ minLength;
133
+ kind = 'minLength';
134
+ constructor(minLength, options) {
135
+ super(options);
136
+ this.minLength = minLength;
161
137
  }
162
138
  }
163
- function setNativeDomProperty(renderer, element, name, value) {
164
- switch (name) {
165
- case 'name':
166
- renderer.setAttribute(element, name, value);
167
- break;
168
- case 'disabled':
169
- case 'readonly':
170
- case 'required':
171
- if (value) {
172
- renderer.setAttribute(element, name, '');
173
- } else {
174
- renderer.removeAttribute(element, name);
175
- }
176
- break;
177
- case 'max':
178
- case 'min':
179
- case 'minLength':
180
- case 'maxLength':
181
- if (value !== undefined) {
182
- renderer.setAttribute(element, name, value.toString());
183
- } else {
184
- renderer.removeAttribute(element, name);
185
- }
186
- break;
139
+ class MaxLengthValidationError extends BaseNgValidationError {
140
+ maxLength;
141
+ kind = 'maxLength';
142
+ constructor(maxLength, options) {
143
+ super(options);
144
+ this.maxLength = maxLength;
187
145
  }
188
146
  }
189
-
190
- const FIELD_STATE_KEY_TO_CONTROL_BINDING = {
191
- disabled: 'disabled',
192
- disabledReasons: 'disabledReasons',
193
- dirty: 'dirty',
194
- errors: 'errors',
195
- hidden: 'hidden',
196
- invalid: 'invalid',
197
- max: 'max',
198
- maxLength: 'maxLength',
199
- min: 'min',
200
- minLength: 'minLength',
201
- name: 'name',
202
- pattern: 'pattern',
203
- pending: 'pending',
204
- readonly: 'readonly',
205
- required: 'required',
206
- touched: 'touched'
207
- };
208
- const CONTROL_BINDING_TO_FIELD_STATE_KEY = /* @__PURE__ */(() => {
209
- const map = {};
210
- for (const key of Object.keys(FIELD_STATE_KEY_TO_CONTROL_BINDING)) {
211
- map[FIELD_STATE_KEY_TO_CONTROL_BINDING[key]] = key;
147
+ class PatternValidationError extends BaseNgValidationError {
148
+ pattern;
149
+ kind = 'pattern';
150
+ constructor(pattern, options) {
151
+ super(options);
152
+ this.pattern = pattern;
212
153
  }
213
- return map;
214
- })();
215
- function readFieldStateBindingValue(fieldState, key) {
216
- const property = CONTROL_BINDING_TO_FIELD_STATE_KEY[key];
217
- return fieldState[property]?.();
218
- }
219
- const CONTROL_BINDING_NAMES = /* @__PURE__ */(() => Object.values(FIELD_STATE_KEY_TO_CONTROL_BINDING))();
220
- function createBindings() {
221
- return {};
222
154
  }
223
- function bindingUpdated(bindings, key, value) {
224
- if (bindings[key] !== value) {
225
- bindings[key] = value;
226
- return true;
227
- }
228
- return false;
155
+ class EmailValidationError extends BaseNgValidationError {
156
+ kind = 'email';
229
157
  }
158
+ const NgValidationError = BaseNgValidationError;
230
159
 
231
- function cvaControlCreate(host, parent) {
232
- parent.controlValueAccessor.registerOnChange(value => parent.state().controlValue.set(value));
233
- parent.controlValueAccessor.registerOnTouched(() => parent.state().markAsTouched());
234
- parent.registerAsBinding();
235
- const bindings = createBindings();
236
- return () => {
237
- const fieldState = parent.state();
238
- const value = fieldState.value();
239
- if (bindingUpdated(bindings, 'controlValue', value)) {
240
- untracked(() => parent.controlValueAccessor.writeValue(value));
160
+ const EMAIL_REGEXP = /^(?=.{1,254}$)(?=.{1,64}@)[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
161
+ function email(path, config) {
162
+ validate(path, ctx => {
163
+ if (isEmpty(ctx.value())) {
164
+ return undefined;
241
165
  }
242
- for (const name of CONTROL_BINDING_NAMES) {
243
- const value = readFieldStateBindingValue(fieldState, name);
244
- if (bindingUpdated(bindings, name, value)) {
245
- const propertyWasSet = host.setInputOnDirectives(name, value);
246
- if (name === 'disabled' && parent.controlValueAccessor.setDisabledState) {
247
- untracked(() => parent.controlValueAccessor.setDisabledState(value));
248
- } else if (!propertyWasSet && parent.elementAcceptsNativeProperty(name)) {
249
- setNativeDomProperty(parent.renderer, parent.nativeFormElement, name, value);
250
- }
166
+ if (!EMAIL_REGEXP.test(ctx.value())) {
167
+ if (config?.error) {
168
+ return getOption(config.error, ctx);
169
+ } else {
170
+ return emailError({
171
+ message: getOption(config?.message, ctx)
172
+ });
251
173
  }
252
174
  }
253
- };
175
+ return undefined;
176
+ });
254
177
  }
255
178
 
256
- function customControlCreate(host, parent) {
257
- host.listenToCustomControlModel(value => parent.state().controlValue.set(value));
258
- host.listenToCustomControlOutput('touchedChange', () => parent.state().markAsTouched());
259
- parent.registerAsBinding(host.customControl);
260
- const bindings = createBindings();
261
- return () => {
262
- const state = parent.state();
263
- const controlValue = state.controlValue();
264
- if (bindingUpdated(bindings, 'controlValue', controlValue)) {
265
- host.setCustomControlModelInput(controlValue);
179
+ function max(path, maxValue, config) {
180
+ const MAX_MEMO = metadata(path, createMetadataKey(), ctx => typeof maxValue === 'number' ? maxValue : maxValue(ctx));
181
+ metadata(path, MAX, ({
182
+ state
183
+ }) => state.metadata(MAX_MEMO)());
184
+ validate(path, ctx => {
185
+ if (isEmpty(ctx.value())) {
186
+ return undefined;
266
187
  }
267
- for (const name of CONTROL_BINDING_NAMES) {
268
- let value;
269
- if (name === 'errors') {
270
- value = parent.errors();
188
+ const max = ctx.state.metadata(MAX_MEMO)();
189
+ if (max === undefined || Number.isNaN(max)) {
190
+ return undefined;
191
+ }
192
+ const value = ctx.value();
193
+ const numValue = !value && value !== 0 ? NaN : Number(value);
194
+ if (numValue > max) {
195
+ if (config?.error) {
196
+ return getOption(config.error, ctx);
271
197
  } else {
272
- value = readFieldStateBindingValue(state, name);
273
- }
274
- if (bindingUpdated(bindings, name, value)) {
275
- host.setInputOnDirectives(name, value);
276
- if (parent.elementAcceptsNativeProperty(name) && !host.customControlHasInput(name)) {
277
- setNativeDomProperty(parent.renderer, parent.nativeFormElement, name, value);
278
- }
198
+ return maxError(max, {
199
+ message: getOption(config?.message, ctx)
200
+ });
279
201
  }
280
202
  }
281
- };
203
+ return undefined;
204
+ });
282
205
  }
283
206
 
284
- function observeSelectMutations(select, onMutation, destroyRef) {
285
- if (typeof MutationObserver !== 'function') {
286
- return;
287
- }
288
- const observer = new MutationObserver(mutations => {
289
- if (mutations.some(m => isRelevantSelectMutation(m))) {
290
- onMutation();
207
+ function maxLength(path, maxLength, config) {
208
+ const MAX_LENGTH_MEMO = metadata(path, createMetadataKey(), ctx => typeof maxLength === 'number' ? maxLength : maxLength(ctx));
209
+ metadata(path, MAX_LENGTH, ({
210
+ state
211
+ }) => state.metadata(MAX_LENGTH_MEMO)());
212
+ validate(path, ctx => {
213
+ if (isEmpty(ctx.value())) {
214
+ return undefined;
291
215
  }
216
+ const maxLength = ctx.state.metadata(MAX_LENGTH_MEMO)();
217
+ if (maxLength === undefined) {
218
+ return undefined;
219
+ }
220
+ if (getLengthOrSize(ctx.value()) > maxLength) {
221
+ if (config?.error) {
222
+ return getOption(config.error, ctx);
223
+ } else {
224
+ return maxLengthError(maxLength, {
225
+ message: getOption(config?.message, ctx)
226
+ });
227
+ }
228
+ }
229
+ return undefined;
292
230
  });
293
- observer.observe(select, {
294
- attributes: true,
295
- attributeFilter: ['value'],
296
- characterData: true,
297
- childList: true,
298
- subtree: true
299
- });
300
- destroyRef.onDestroy(() => observer.disconnect());
301
231
  }
302
- function isRelevantSelectMutation(mutation) {
303
- if (mutation.type === 'childList' || mutation.type === 'characterData') {
304
- if (mutation.target instanceof Comment) {
305
- return false;
232
+
233
+ function min(path, minValue, config) {
234
+ const MIN_MEMO = metadata(path, createMetadataKey(), ctx => typeof minValue === 'number' ? minValue : minValue(ctx));
235
+ metadata(path, MIN, ({
236
+ state
237
+ }) => state.metadata(MIN_MEMO)());
238
+ validate(path, ctx => {
239
+ if (isEmpty(ctx.value())) {
240
+ return undefined;
306
241
  }
307
- for (const node of mutation.addedNodes) {
308
- if (!(node instanceof Comment)) {
309
- return true;
310
- }
242
+ const min = ctx.state.metadata(MIN_MEMO)();
243
+ if (min === undefined || Number.isNaN(min)) {
244
+ return undefined;
311
245
  }
312
- for (const node of mutation.removedNodes) {
313
- if (!(node instanceof Comment)) {
314
- return true;
246
+ const value = ctx.value();
247
+ const numValue = !value && value !== 0 ? NaN : Number(value);
248
+ if (numValue < min) {
249
+ if (config?.error) {
250
+ return getOption(config.error, ctx);
251
+ } else {
252
+ return minError(min, {
253
+ message: getOption(config?.message, ctx)
254
+ });
315
255
  }
316
256
  }
317
- return false;
318
- }
319
- if (mutation.type === 'attributes' && mutation.target instanceof HTMLOptionElement) {
320
- return true;
321
- }
322
- return false;
257
+ return undefined;
258
+ });
323
259
  }
324
260
 
325
- function nativeControlCreate(host, parent) {
326
- let updateMode = false;
327
- const input = parent.nativeFormElement;
328
- host.listenToDom('input', () => {
329
- const state = parent.state();
330
- state.controlValue.set(getNativeControlValue(input, state.value));
331
- });
332
- host.listenToDom('blur', () => parent.state().markAsTouched());
333
- parent.registerAsBinding();
334
- if (input.tagName === 'SELECT') {
335
- observeSelectMutations(input, () => {
336
- if (!updateMode) {
337
- return;
338
- }
339
- input.value = parent.state().controlValue();
340
- }, parent.destroyRef);
341
- }
342
- const bindings = createBindings();
343
- return () => {
344
- const state = parent.state();
345
- const controlValue = state.controlValue();
346
- if (bindingUpdated(bindings, 'controlValue', controlValue)) {
347
- setNativeControlValue(input, controlValue);
261
+ function minLength(path, minLength, config) {
262
+ const MIN_LENGTH_MEMO = metadata(path, createMetadataKey(), ctx => typeof minLength === 'number' ? minLength : minLength(ctx));
263
+ metadata(path, MIN_LENGTH, ({
264
+ state
265
+ }) => state.metadata(MIN_LENGTH_MEMO)());
266
+ validate(path, ctx => {
267
+ if (isEmpty(ctx.value())) {
268
+ return undefined;
348
269
  }
349
- for (const name of CONTROL_BINDING_NAMES) {
350
- const value = readFieldStateBindingValue(state, name);
351
- if (bindingUpdated(bindings, name, value)) {
352
- host.setInputOnDirectives(name, value);
353
- if (parent.elementAcceptsNativeProperty(name)) {
354
- setNativeDomProperty(parent.renderer, input, name, value);
355
- }
270
+ const minLength = ctx.state.metadata(MIN_LENGTH_MEMO)();
271
+ if (minLength === undefined) {
272
+ return undefined;
273
+ }
274
+ if (getLengthOrSize(ctx.value()) < minLength) {
275
+ if (config?.error) {
276
+ return getOption(config.error, ctx);
277
+ } else {
278
+ return minLengthError(minLength, {
279
+ message: getOption(config?.message, ctx)
280
+ });
356
281
  }
357
282
  }
358
- updateMode = true;
359
- };
283
+ return undefined;
284
+ });
360
285
  }
361
286
 
362
- const ɵNgFieldDirective = Symbol();
363
- const FORM_FIELD = new InjectionToken(typeof ngDevMode !== 'undefined' && ngDevMode ? 'FORM_FIELD' : '');
364
- class FormField {
365
- fieldTree = input.required({
366
- ...(ngDevMode ? {
367
- debugName: "fieldTree"
368
- } : {}),
369
- alias: 'formField'
370
- });
371
- renderer = inject(Renderer2);
372
- destroyRef = inject(DestroyRef);
373
- state = computed(() => this.fieldTree()(), ...(ngDevMode ? [{
374
- debugName: "state"
375
- }] : []));
376
- injector = inject(Injector);
377
- element = inject(ElementRef).nativeElement;
378
- elementIsNativeFormElement = isNativeFormElement(this.element);
379
- elementAcceptsNumericValues = isNumericFormElement(this.element);
380
- elementAcceptsTextualValues = isTextualFormElement(this.element);
381
- nativeFormElement = this.elementIsNativeFormElement ? this.element : undefined;
382
- focuser = options => this.element.focus(options);
383
- controlValueAccessors = inject(NG_VALUE_ACCESSOR, {
384
- optional: true,
385
- self: true
386
- });
387
- config = inject(SIGNAL_FORMS_CONFIG, {
388
- optional: true
389
- });
390
- parseErrorsSource = signal(undefined, ...(ngDevMode ? [{
391
- debugName: "parseErrorsSource"
392
- }] : []));
393
- _interopNgControl;
394
- get interopNgControl() {
395
- return this._interopNgControl ??= new InteropNgControl(this.state);
396
- }
397
- parseErrors = computed(() => this.parseErrorsSource()?.().map(err => ({
398
- ...err,
399
- fieldTree: untracked(this.fieldTree),
400
- formField: this
401
- })) ?? [], ...(ngDevMode ? [{
402
- debugName: "parseErrors"
403
- }] : []));
404
- errors = computed(() => this.state().errors().filter(err => !err.formField || err.formField === this), ...(ngDevMode ? [{
405
- debugName: "errors"
406
- }] : []));
407
- isFieldBinding = false;
408
- get controlValueAccessor() {
409
- return this.controlValueAccessors?.[0] ?? this.interopNgControl?.valueAccessor ?? undefined;
410
- }
411
- installClassBindingEffect() {
412
- const classes = Object.entries(this.config?.classes ?? {}).map(([className, computation]) => [className, computed(() => computation(this))]);
413
- if (classes.length === 0) {
414
- return;
415
- }
416
- const bindings = createBindings();
417
- afterRenderEffect({
418
- write: () => {
419
- for (const [className, computation] of classes) {
420
- const active = computation();
421
- if (bindingUpdated(bindings, className, active)) {
422
- if (active) {
423
- this.renderer.addClass(this.element, className);
424
- } else {
425
- this.renderer.removeClass(this.element, className);
426
- }
427
- }
428
- }
429
- }
430
- }, {
431
- injector: this.injector
432
- });
433
- }
434
- focus(options) {
435
- this.focuser(options);
436
- }
437
- registerAsBinding(bindingOptions) {
438
- if (this.isFieldBinding) {
439
- throw new _RuntimeError(1913, ngDevMode && 'FormField already registered as a binding');
440
- }
441
- this.isFieldBinding = true;
442
- this.installClassBindingEffect();
443
- if (bindingOptions?.focus) {
444
- this.focuser = bindingOptions.focus;
445
- }
446
- if (bindingOptions?.parseErrors) {
447
- this.parseErrorsSource.set(bindingOptions.parseErrors);
448
- }
449
- effect(onCleanup => {
450
- const fieldNode = this.state();
451
- fieldNode.nodeState.formFieldBindings.update(controls => [...controls, this]);
452
- onCleanup(() => {
453
- fieldNode.nodeState.formFieldBindings.update(controls => controls.filter(c => c !== this));
454
- });
455
- }, {
456
- injector: this.injector
457
- });
458
- }
459
- [ɵNgFieldDirective];
460
- ɵngControlCreate(host) {
461
- if (host.hasPassThrough) {
462
- return;
463
- }
464
- if (this.controlValueAccessor) {
465
- this.ɵngControlUpdate = cvaControlCreate(host, this);
466
- } else if (host.customControl) {
467
- this.ɵngControlUpdate = customControlCreate(host, this);
468
- } else if (this.elementIsNativeFormElement) {
469
- this.ɵngControlUpdate = nativeControlCreate(host, this);
470
- } else {
471
- throw new _RuntimeError(1914, 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.`);
287
+ function pattern(path, pattern, config) {
288
+ const PATTERN_MEMO = metadata(path, createMetadataKey(), ctx => pattern instanceof RegExp ? pattern : pattern(ctx));
289
+ metadata(path, PATTERN, ({
290
+ state
291
+ }) => state.metadata(PATTERN_MEMO)());
292
+ validate(path, ctx => {
293
+ if (isEmpty(ctx.value())) {
294
+ return undefined;
472
295
  }
473
- }
474
- ɵngControlUpdate;
475
- elementAcceptsNativeProperty(key) {
476
- if (!this.elementIsNativeFormElement) {
477
- return false;
296
+ const pattern = ctx.state.metadata(PATTERN_MEMO)();
297
+ if (pattern === undefined) {
298
+ return undefined;
478
299
  }
479
- switch (key) {
480
- case 'min':
481
- case 'max':
482
- return this.elementAcceptsNumericValues;
483
- case 'minLength':
484
- case 'maxLength':
485
- return this.elementAcceptsTextualValues;
486
- case 'disabled':
487
- case 'required':
488
- case 'readonly':
489
- case 'name':
490
- return true;
491
- default:
492
- return false;
300
+ if (!pattern.test(ctx.value())) {
301
+ if (config?.error) {
302
+ return getOption(config.error, ctx);
303
+ } else {
304
+ return patternError(pattern, {
305
+ message: getOption(config?.message, ctx)
306
+ });
307
+ }
493
308
  }
494
- }
495
- static ɵfac = i0.ɵɵngDeclareFactory({
496
- minVersion: "12.0.0",
497
- version: "21.2.0-next.2",
498
- ngImport: i0,
499
- type: FormField,
500
- deps: [],
501
- target: i0.ɵɵFactoryTarget.Directive
309
+ return undefined;
502
310
  });
503
- static ɵdir = i0.ɵɵngDeclareDirective({
504
- minVersion: "17.1.0",
505
- version: "21.2.0-next.2",
506
- type: FormField,
507
- isStandalone: true,
508
- selector: "[formField]",
509
- inputs: {
510
- fieldTree: {
511
- classPropertyName: "fieldTree",
512
- publicName: "formField",
513
- isSignal: true,
514
- isRequired: true,
515
- transformFunction: null
311
+ }
312
+
313
+ function required(path, config) {
314
+ const REQUIRED_MEMO = metadata(path, createMetadataKey(), ctx => config?.when ? config.when(ctx) : true);
315
+ metadata(path, REQUIRED, ({
316
+ state
317
+ }) => state.metadata(REQUIRED_MEMO)());
318
+ validate(path, ctx => {
319
+ if (ctx.state.metadata(REQUIRED_MEMO)() && isEmpty(ctx.value())) {
320
+ if (config?.error) {
321
+ return getOption(config.error, ctx);
322
+ } else {
323
+ return requiredError({
324
+ message: getOption(config?.message, ctx)
325
+ });
516
326
  }
517
- },
518
- providers: [{
519
- provide: FORM_FIELD,
520
- useExisting: FormField
521
- }, {
522
- provide: NgControl,
523
- useFactory: () => inject(FormField).interopNgControl
524
- }],
525
- exportAs: ["formField"],
526
- controlCreate: {
527
- passThroughInput: "formField"
528
- },
529
- ngImport: i0
327
+ }
328
+ return undefined;
530
329
  });
531
330
  }
532
- i0.ɵɵngDeclareClassMetadata({
533
- minVersion: "12.0.0",
534
- version: "21.2.0-next.2",
535
- ngImport: i0,
536
- type: FormField,
537
- decorators: [{
538
- type: Directive,
539
- args: [{
540
- selector: '[formField]',
541
- exportAs: 'formField',
542
- providers: [{
543
- provide: FORM_FIELD,
544
- useExisting: FormField
545
- }, {
546
- provide: NgControl,
547
- useFactory: () => inject(FormField).interopNgControl
548
- }]
549
- }]
550
- }],
551
- propDecorators: {
552
- fieldTree: [{
553
- type: i0.Input,
554
- args: [{
555
- isSignal: true,
556
- alias: "formField",
557
- required: true
558
- }]
559
- }]
560
- }
561
- });
562
331
 
563
- function disabled(path, logic) {
332
+ function validateAsync(path, opts) {
564
333
  assertPathIsCurrent(path);
565
334
  const pathNode = FieldPathNode.unwrapFieldPath(path);
566
- pathNode.builder.addDisabledReasonRule(ctx => {
567
- let result = true;
568
- if (typeof logic === 'string') {
569
- result = logic;
570
- } else if (logic) {
571
- result = logic(ctx);
335
+ const RESOURCE = createManagedMetadataKey(opts.factory);
336
+ metadata(path, RESOURCE, ctx => {
337
+ const node = ctx.stateOf(path);
338
+ const validationState = node.validationState;
339
+ if (validationState.shouldSkipValidation() || !validationState.syncValid()) {
340
+ return undefined;
572
341
  }
573
- if (typeof result === 'string') {
574
- return {
575
- fieldTree: ctx.fieldTree,
576
- message: result
577
- };
342
+ return opts.params(ctx);
343
+ });
344
+ pathNode.builder.addAsyncErrorRule(ctx => {
345
+ const res = ctx.state.metadata(RESOURCE);
346
+ let errors;
347
+ switch (res.status()) {
348
+ case 'idle':
349
+ return undefined;
350
+ case 'loading':
351
+ case 'reloading':
352
+ return 'pending';
353
+ case 'resolved':
354
+ case 'local':
355
+ if (!res.hasValue()) {
356
+ return undefined;
357
+ }
358
+ errors = opts.onSuccess(res.value(), ctx);
359
+ return addDefaultField(errors, ctx.fieldTree);
360
+ case 'error':
361
+ errors = opts.onError(res.error(), ctx);
362
+ return addDefaultField(errors, ctx.fieldTree);
578
363
  }
579
- return result ? {
580
- fieldTree: ctx.fieldTree
581
- } : undefined;
582
364
  });
583
365
  }
584
366
 
585
- function hidden(path, logic) {
367
+ function validateTree(path, logic) {
586
368
  assertPathIsCurrent(path);
587
369
  const pathNode = FieldPathNode.unwrapFieldPath(path);
588
- pathNode.builder.addHiddenRule(logic);
370
+ pathNode.builder.addSyncTreeErrorRule(ctx => addDefaultField(logic(ctx), ctx.fieldTree));
589
371
  }
590
372
 
591
- function readonly(path, logic = () => true) {
592
- assertPathIsCurrent(path);
593
- const pathNode = FieldPathNode.unwrapFieldPath(path);
594
- pathNode.builder.addReadonlyRule(logic);
373
+ function validateStandardSchema(path, schema) {
374
+ const VALIDATOR_MEMO = metadata(path, createMetadataKey(), ctx => {
375
+ const resolvedSchema = typeof schema === 'function' ? schema(ctx) : schema;
376
+ return resolvedSchema ? resolvedSchema['~standard'].validate(ctx.value()) : undefined;
377
+ });
378
+ validateTree(path, ({
379
+ state,
380
+ fieldTreeOf
381
+ }) => {
382
+ const result = state.metadata(VALIDATOR_MEMO)();
383
+ if (!result || _isPromise(result)) {
384
+ return [];
385
+ }
386
+ return result?.issues?.map(issue => standardIssueToFormTreeError(fieldTreeOf(path), issue)) ?? [];
387
+ });
388
+ validateAsync(path, {
389
+ params: ({
390
+ state
391
+ }) => {
392
+ const result = state.metadata(VALIDATOR_MEMO)();
393
+ return result && _isPromise(result) ? result : undefined;
394
+ },
395
+ factory: params => {
396
+ return resource({
397
+ params,
398
+ loader: async ({
399
+ params
400
+ }) => (await params)?.issues ?? []
401
+ });
402
+ },
403
+ onSuccess: (issues, {
404
+ fieldTreeOf
405
+ }) => {
406
+ return issues.map(issue => standardIssueToFormTreeError(fieldTreeOf(path), issue));
407
+ },
408
+ onError: () => {}
409
+ });
595
410
  }
596
-
597
- function getLengthOrSize(value) {
598
- const v = value;
599
- return typeof v.length === 'number' ? v.length : v.size;
411
+ function standardSchemaError(issue, options) {
412
+ return new StandardSchemaValidationError(issue, options);
600
413
  }
601
- function getOption(opt, ctx) {
602
- return opt instanceof Function ? opt(ctx) : opt;
414
+ function standardIssueToFormTreeError(fieldTree, issue) {
415
+ let target = fieldTree;
416
+ for (const pathPart of issue.path ?? []) {
417
+ const pathKey = typeof pathPart === 'object' ? pathPart.key : pathPart;
418
+ target = target[pathKey];
419
+ }
420
+ return addDefaultField(standardSchemaError(issue, {
421
+ message: issue.message
422
+ }), target);
603
423
  }
604
- function isEmpty(value) {
605
- if (typeof value === 'number') {
606
- return isNaN(value);
424
+ class StandardSchemaValidationError extends BaseNgValidationError {
425
+ issue;
426
+ kind = 'standardSchema';
427
+ constructor(issue, options) {
428
+ super(options);
429
+ this.issue = issue;
607
430
  }
608
- return value === '' || value === false || value == null;
609
431
  }
610
432
 
611
- function validate(path, logic) {
612
- assertPathIsCurrent(path);
613
- const pathNode = FieldPathNode.unwrapFieldPath(path);
614
- pathNode.builder.addSyncErrorRule(ctx => {
615
- return addDefaultField(logic(ctx), ctx.fieldTree);
433
+ function validateHttp(path, opts) {
434
+ validateAsync(path, {
435
+ params: opts.request,
436
+ factory: request => httpResource(request, opts.options),
437
+ onSuccess: opts.onSuccess,
438
+ onError: opts.onError
616
439
  });
617
440
  }
618
441
 
619
- function requiredError(options) {
620
- return new RequiredValidationError(options);
621
- }
622
- function minError(min, options) {
623
- return new MinValidationError(min, options);
624
- }
625
- function maxError(max, options) {
626
- return new MaxValidationError(max, options);
627
- }
628
- function minLengthError(minLength, options) {
629
- return new MinLengthValidationError(minLength, options);
630
- }
631
- function maxLengthError(maxLength, options) {
632
- return new MaxLengthValidationError(maxLength, options);
442
+ function debounce(path, durationOrDebouncer) {
443
+ assertPathIsCurrent(path);
444
+ const pathNode = FieldPathNode.unwrapFieldPath(path);
445
+ const debouncer = typeof durationOrDebouncer === 'function' ? durationOrDebouncer : durationOrDebouncer > 0 ? debounceForDuration(durationOrDebouncer) : immediate;
446
+ pathNode.builder.addMetadataRule(DEBOUNCER, () => debouncer);
633
447
  }
634
- function patternError(pattern, options) {
635
- return new PatternValidationError(pattern, options);
448
+ function debounceForDuration(durationInMilliseconds) {
449
+ return (_context, abortSignal) => {
450
+ return new Promise(resolve => {
451
+ let timeoutId;
452
+ const onAbort = () => {
453
+ clearTimeout(timeoutId);
454
+ resolve();
455
+ };
456
+ timeoutId = setTimeout(() => {
457
+ abortSignal.removeEventListener('abort', onAbort);
458
+ resolve();
459
+ }, durationInMilliseconds);
460
+ abortSignal.addEventListener('abort', onAbort, {
461
+ once: true
462
+ });
463
+ });
464
+ };
636
465
  }
637
- function emailError(options) {
638
- return new EmailValidationError(options);
466
+ function immediate() {}
467
+
468
+ const FORM_FIELD_PARSE_ERRORS = new InjectionToken(typeof ngDevMode !== 'undefined' && ngDevMode ? 'FORM_FIELD_PARSE_ERRORS' : '');
469
+
470
+ function transformedValue(value, options) {
471
+ const {
472
+ parse,
473
+ format
474
+ } = options;
475
+ const parseErrors = signal([], ...(ngDevMode ? [{
476
+ debugName: "parseErrors"
477
+ }] : []));
478
+ const rawValue = linkedSignal(() => format(value()), ...(ngDevMode ? [{
479
+ debugName: "rawValue"
480
+ }] : []));
481
+ const formFieldParseErrors = inject(FORM_FIELD_PARSE_ERRORS, {
482
+ self: true,
483
+ optional: true
484
+ });
485
+ if (formFieldParseErrors) {
486
+ formFieldParseErrors.set(parseErrors);
487
+ }
488
+ const result = rawValue;
489
+ const originalSet = result.set.bind(result);
490
+ result.set = newRawValue => {
491
+ const result = parse(newRawValue);
492
+ parseErrors.set(result.errors ?? []);
493
+ if (result.value !== undefined) {
494
+ value.set(result.value);
495
+ }
496
+ originalSet(newRawValue);
497
+ };
498
+ result.update = updateFn => {
499
+ result.set(updateFn(rawValue()));
500
+ };
501
+ result.parseErrors = parseErrors.asReadonly();
502
+ return result;
639
503
  }
640
- class BaseNgValidationError {
641
- __brand = undefined;
642
- kind = '';
643
- fieldTree;
644
- message;
645
- constructor(options) {
646
- if (options) {
647
- Object.assign(this, options);
504
+
505
+ class InteropNgControl {
506
+ field;
507
+ constructor(field) {
508
+ this.field = field;
509
+ }
510
+ control = this;
511
+ get value() {
512
+ return this.field().value();
513
+ }
514
+ get valid() {
515
+ return this.field().valid();
516
+ }
517
+ get invalid() {
518
+ return this.field().invalid();
519
+ }
520
+ get pending() {
521
+ return this.field().pending();
522
+ }
523
+ get disabled() {
524
+ return this.field().disabled();
525
+ }
526
+ get enabled() {
527
+ return !this.field().disabled();
528
+ }
529
+ get errors() {
530
+ return signalErrorsToValidationErrors(this.field().errors());
531
+ }
532
+ get pristine() {
533
+ return !this.field().dirty();
534
+ }
535
+ get dirty() {
536
+ return this.field().dirty();
537
+ }
538
+ get touched() {
539
+ return this.field().touched();
540
+ }
541
+ get untouched() {
542
+ return !this.field().touched();
543
+ }
544
+ get status() {
545
+ if (this.field().disabled()) {
546
+ return 'DISABLED';
547
+ }
548
+ if (this.field().valid()) {
549
+ return 'VALID';
550
+ }
551
+ if (this.field().invalid()) {
552
+ return 'INVALID';
553
+ }
554
+ if (this.field().pending()) {
555
+ return 'PENDING';
556
+ }
557
+ throw new _RuntimeError(1910, ngDevMode && 'Unknown form control status');
558
+ }
559
+ valueAccessor = null;
560
+ hasValidator(validator) {
561
+ if (validator === Validators.required) {
562
+ return this.field().required();
648
563
  }
564
+ return false;
649
565
  }
566
+ updateValueAndValidity() {}
650
567
  }
651
- class RequiredValidationError extends BaseNgValidationError {
652
- kind = 'required';
653
- }
654
- class MinValidationError extends BaseNgValidationError {
655
- min;
656
- kind = 'min';
657
- constructor(min, options) {
658
- super(options);
659
- this.min = min;
568
+
569
+ const FIELD_STATE_KEY_TO_CONTROL_BINDING = {
570
+ disabled: 'disabled',
571
+ disabledReasons: 'disabledReasons',
572
+ dirty: 'dirty',
573
+ errors: 'errors',
574
+ hidden: 'hidden',
575
+ invalid: 'invalid',
576
+ max: 'max',
577
+ maxLength: 'maxLength',
578
+ min: 'min',
579
+ minLength: 'minLength',
580
+ name: 'name',
581
+ pattern: 'pattern',
582
+ pending: 'pending',
583
+ readonly: 'readonly',
584
+ required: 'required',
585
+ touched: 'touched'
586
+ };
587
+ const CONTROL_BINDING_TO_FIELD_STATE_KEY = /* @__PURE__ */(() => {
588
+ const map = {};
589
+ for (const key of Object.keys(FIELD_STATE_KEY_TO_CONTROL_BINDING)) {
590
+ map[FIELD_STATE_KEY_TO_CONTROL_BINDING[key]] = key;
660
591
  }
592
+ return map;
593
+ })();
594
+ function readFieldStateBindingValue(fieldState, key) {
595
+ const property = CONTROL_BINDING_TO_FIELD_STATE_KEY[key];
596
+ return fieldState[property]?.();
661
597
  }
662
- class MaxValidationError extends BaseNgValidationError {
663
- max;
664
- kind = 'max';
665
- constructor(max, options) {
666
- super(options);
667
- this.max = max;
598
+ const CONTROL_BINDING_NAMES = /* @__PURE__ */(() => Object.values(FIELD_STATE_KEY_TO_CONTROL_BINDING))();
599
+ function createBindings() {
600
+ return {};
601
+ }
602
+ function bindingUpdated(bindings, key, value) {
603
+ if (bindings[key] !== value) {
604
+ bindings[key] = value;
605
+ return true;
668
606
  }
607
+ return false;
669
608
  }
670
- class MinLengthValidationError extends BaseNgValidationError {
671
- minLength;
672
- kind = 'minLength';
673
- constructor(minLength, options) {
674
- super(options);
675
- this.minLength = minLength;
609
+
610
+ function isNativeFormElement(element) {
611
+ return element.tagName === 'INPUT' || element.tagName === 'SELECT' || element.tagName === 'TEXTAREA';
612
+ }
613
+ function isNumericFormElement(element) {
614
+ if (element.tagName !== 'INPUT') {
615
+ return false;
676
616
  }
617
+ const type = element.type;
618
+ return type === 'date' || type === 'datetime-local' || type === 'month' || type === 'number' || type === 'range' || type === 'time' || type === 'week';
677
619
  }
678
- class MaxLengthValidationError extends BaseNgValidationError {
679
- maxLength;
680
- kind = 'maxLength';
681
- constructor(maxLength, options) {
682
- super(options);
683
- this.maxLength = maxLength;
620
+ function isTextualFormElement(element) {
621
+ return element.tagName === 'INPUT' || element.tagName === 'TEXTAREA';
622
+ }
623
+ function getNativeControlValue(element, currentValue) {
624
+ switch (element.type) {
625
+ case 'checkbox':
626
+ return element.checked;
627
+ case 'number':
628
+ case 'range':
629
+ case 'datetime-local':
630
+ if (typeof untracked(currentValue) === 'number') {
631
+ return element.valueAsNumber;
632
+ }
633
+ break;
634
+ case 'date':
635
+ case 'month':
636
+ case 'time':
637
+ case 'week':
638
+ const value = untracked(currentValue);
639
+ if (value === null || value instanceof Date) {
640
+ return element.valueAsDate;
641
+ } else if (typeof value === 'number') {
642
+ return element.valueAsNumber;
643
+ }
644
+ break;
684
645
  }
646
+ return element.value;
685
647
  }
686
- class PatternValidationError extends BaseNgValidationError {
687
- pattern;
688
- kind = 'pattern';
689
- constructor(pattern, options) {
690
- super(options);
691
- this.pattern = pattern;
648
+ function setNativeControlValue(element, value) {
649
+ switch (element.type) {
650
+ case 'checkbox':
651
+ element.checked = value;
652
+ return;
653
+ case 'radio':
654
+ element.checked = value === element.value;
655
+ return;
656
+ case 'number':
657
+ case 'range':
658
+ case 'datetime-local':
659
+ if (typeof value === 'number') {
660
+ setNativeNumberControlValue(element, value);
661
+ return;
662
+ }
663
+ break;
664
+ case 'date':
665
+ case 'month':
666
+ case 'time':
667
+ case 'week':
668
+ if (value === null || value instanceof Date) {
669
+ element.valueAsDate = value;
670
+ return;
671
+ } else if (typeof value === 'number') {
672
+ setNativeNumberControlValue(element, value);
673
+ return;
674
+ }
692
675
  }
676
+ element.value = value;
693
677
  }
694
- class EmailValidationError extends BaseNgValidationError {
695
- kind = 'email';
678
+ function setNativeNumberControlValue(element, value) {
679
+ if (isNaN(value)) {
680
+ element.value = '';
681
+ } else {
682
+ element.valueAsNumber = value;
683
+ }
696
684
  }
697
- const NgValidationError = BaseNgValidationError;
698
-
699
- const EMAIL_REGEXP = /^(?=.{1,254}$)(?=.{1,64}@)[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
700
- function email(path, config) {
701
- validate(path, ctx => {
702
- if (isEmpty(ctx.value())) {
703
- return undefined;
704
- }
705
- if (!EMAIL_REGEXP.test(ctx.value())) {
706
- if (config?.error) {
707
- return getOption(config.error, ctx);
685
+ function setNativeDomProperty(renderer, element, name, value) {
686
+ switch (name) {
687
+ case 'name':
688
+ renderer.setAttribute(element, name, value);
689
+ break;
690
+ case 'disabled':
691
+ case 'readonly':
692
+ case 'required':
693
+ if (value) {
694
+ renderer.setAttribute(element, name, '');
708
695
  } else {
709
- return emailError({
710
- message: getOption(config?.message, ctx)
711
- });
696
+ renderer.removeAttribute(element, name);
712
697
  }
713
- }
714
- return undefined;
715
- });
698
+ break;
699
+ case 'max':
700
+ case 'min':
701
+ case 'minLength':
702
+ case 'maxLength':
703
+ if (value !== undefined) {
704
+ renderer.setAttribute(element, name, value.toString());
705
+ } else {
706
+ renderer.removeAttribute(element, name);
707
+ }
708
+ break;
709
+ }
716
710
  }
717
711
 
718
- function max(path, maxValue, config) {
719
- const MAX_MEMO = metadata(path, createMetadataKey(), ctx => typeof maxValue === 'number' ? maxValue : maxValue(ctx));
720
- metadata(path, MAX, ({
721
- state
722
- }) => state.metadata(MAX_MEMO)());
723
- validate(path, ctx => {
724
- if (isEmpty(ctx.value())) {
725
- return undefined;
726
- }
727
- const max = ctx.state.metadata(MAX_MEMO)();
728
- if (max === undefined || Number.isNaN(max)) {
729
- return undefined;
712
+ function customControlCreate(host, parent) {
713
+ host.listenToCustomControlModel(value => parent.state().controlValue.set(value));
714
+ host.listenToCustomControlOutput('touchedChange', () => parent.state().markAsTouched());
715
+ parent.registerAsBinding(host.customControl);
716
+ const bindings = createBindings();
717
+ return () => {
718
+ const state = parent.state();
719
+ const controlValue = state.controlValue();
720
+ if (bindingUpdated(bindings, 'controlValue', controlValue)) {
721
+ host.setCustomControlModelInput(controlValue);
730
722
  }
731
- const value = ctx.value();
732
- const numValue = !value && value !== 0 ? NaN : Number(value);
733
- if (numValue > max) {
734
- if (config?.error) {
735
- return getOption(config.error, ctx);
723
+ for (const name of CONTROL_BINDING_NAMES) {
724
+ let value;
725
+ if (name === 'errors') {
726
+ value = parent.errors();
736
727
  } else {
737
- return maxError(max, {
738
- message: getOption(config?.message, ctx)
739
- });
728
+ value = readFieldStateBindingValue(state, name);
729
+ }
730
+ if (bindingUpdated(bindings, name, value)) {
731
+ host.setInputOnDirectives(name, value);
732
+ if (parent.elementAcceptsNativeProperty(name) && !host.customControlHasInput(name)) {
733
+ setNativeDomProperty(parent.renderer, parent.nativeFormElement, name, value);
734
+ }
740
735
  }
741
736
  }
742
- return undefined;
743
- });
737
+ };
744
738
  }
745
739
 
746
- function maxLength(path, maxLength, config) {
747
- const MAX_LENGTH_MEMO = metadata(path, createMetadataKey(), ctx => typeof maxLength === 'number' ? maxLength : maxLength(ctx));
748
- metadata(path, MAX_LENGTH, ({
749
- state
750
- }) => state.metadata(MAX_LENGTH_MEMO)());
751
- validate(path, ctx => {
752
- if (isEmpty(ctx.value())) {
753
- return undefined;
754
- }
755
- const maxLength = ctx.state.metadata(MAX_LENGTH_MEMO)();
756
- if (maxLength === undefined) {
757
- return undefined;
758
- }
759
- if (getLengthOrSize(ctx.value()) > maxLength) {
760
- if (config?.error) {
761
- return getOption(config.error, ctx);
762
- } else {
763
- return maxLengthError(maxLength, {
764
- message: getOption(config?.message, ctx)
765
- });
740
+ function cvaControlCreate(host, parent) {
741
+ parent.controlValueAccessor.registerOnChange(value => parent.state().controlValue.set(value));
742
+ parent.controlValueAccessor.registerOnTouched(() => parent.state().markAsTouched());
743
+ parent.registerAsBinding();
744
+ const bindings = createBindings();
745
+ return () => {
746
+ const fieldState = parent.state();
747
+ const value = fieldState.value();
748
+ if (bindingUpdated(bindings, 'controlValue', value)) {
749
+ untracked(() => parent.controlValueAccessor.writeValue(value));
750
+ }
751
+ for (const name of CONTROL_BINDING_NAMES) {
752
+ const value = readFieldStateBindingValue(fieldState, name);
753
+ if (bindingUpdated(bindings, name, value)) {
754
+ const propertyWasSet = host.setInputOnDirectives(name, value);
755
+ if (name === 'disabled' && parent.controlValueAccessor.setDisabledState) {
756
+ untracked(() => parent.controlValueAccessor.setDisabledState(value));
757
+ } else if (!propertyWasSet && parent.elementAcceptsNativeProperty(name)) {
758
+ setNativeDomProperty(parent.renderer, parent.nativeFormElement, name, value);
759
+ }
766
760
  }
767
761
  }
768
- return undefined;
769
- });
762
+ };
770
763
  }
771
764
 
772
- function min(path, minValue, config) {
773
- const MIN_MEMO = metadata(path, createMetadataKey(), ctx => typeof minValue === 'number' ? minValue : minValue(ctx));
774
- metadata(path, MIN, ({
775
- state
776
- }) => state.metadata(MIN_MEMO)());
777
- validate(path, ctx => {
778
- if (isEmpty(ctx.value())) {
779
- return undefined;
780
- }
781
- const min = ctx.state.metadata(MIN_MEMO)();
782
- if (min === undefined || Number.isNaN(min)) {
783
- return undefined;
784
- }
785
- const value = ctx.value();
786
- const numValue = !value && value !== 0 ? NaN : Number(value);
787
- if (numValue < min) {
788
- if (config?.error) {
789
- return getOption(config.error, ctx);
790
- } else {
791
- return minError(min, {
792
- message: getOption(config?.message, ctx)
793
- });
794
- }
765
+ function observeSelectMutations(select, onMutation, destroyRef) {
766
+ if (typeof MutationObserver !== 'function') {
767
+ return;
768
+ }
769
+ const observer = new MutationObserver(mutations => {
770
+ if (mutations.some(m => isRelevantSelectMutation(m))) {
771
+ onMutation();
795
772
  }
796
- return undefined;
797
773
  });
774
+ observer.observe(select, {
775
+ attributes: true,
776
+ attributeFilter: ['value'],
777
+ characterData: true,
778
+ childList: true,
779
+ subtree: true
780
+ });
781
+ destroyRef.onDestroy(() => observer.disconnect());
798
782
  }
799
-
800
- function minLength(path, minLength, config) {
801
- const MIN_LENGTH_MEMO = metadata(path, createMetadataKey(), ctx => typeof minLength === 'number' ? minLength : minLength(ctx));
802
- metadata(path, MIN_LENGTH, ({
803
- state
804
- }) => state.metadata(MIN_LENGTH_MEMO)());
805
- validate(path, ctx => {
806
- if (isEmpty(ctx.value())) {
807
- return undefined;
783
+ function isRelevantSelectMutation(mutation) {
784
+ if (mutation.type === 'childList' || mutation.type === 'characterData') {
785
+ if (mutation.target instanceof Comment) {
786
+ return false;
808
787
  }
809
- const minLength = ctx.state.metadata(MIN_LENGTH_MEMO)();
810
- if (minLength === undefined) {
811
- return undefined;
788
+ for (const node of mutation.addedNodes) {
789
+ if (!(node instanceof Comment)) {
790
+ return true;
791
+ }
812
792
  }
813
- if (getLengthOrSize(ctx.value()) < minLength) {
814
- if (config?.error) {
815
- return getOption(config.error, ctx);
816
- } else {
817
- return minLengthError(minLength, {
818
- message: getOption(config?.message, ctx)
819
- });
793
+ for (const node of mutation.removedNodes) {
794
+ if (!(node instanceof Comment)) {
795
+ return true;
820
796
  }
821
797
  }
822
- return undefined;
823
- });
798
+ return false;
799
+ }
800
+ if (mutation.type === 'attributes' && mutation.target instanceof HTMLOptionElement) {
801
+ return true;
802
+ }
803
+ return false;
824
804
  }
825
805
 
826
- function pattern(path, pattern, config) {
827
- const PATTERN_MEMO = metadata(path, createMetadataKey(), ctx => pattern instanceof RegExp ? pattern : pattern(ctx));
828
- metadata(path, PATTERN, ({
829
- state
830
- }) => state.metadata(PATTERN_MEMO)());
831
- validate(path, ctx => {
832
- if (isEmpty(ctx.value())) {
833
- return undefined;
834
- }
835
- const pattern = ctx.state.metadata(PATTERN_MEMO)();
836
- if (pattern === undefined) {
837
- return undefined;
806
+ function nativeControlCreate(host, parent) {
807
+ let updateMode = false;
808
+ const input = parent.nativeFormElement;
809
+ host.listenToDom('input', () => {
810
+ const state = parent.state();
811
+ state.controlValue.set(getNativeControlValue(input, state.value));
812
+ });
813
+ host.listenToDom('blur', () => parent.state().markAsTouched());
814
+ parent.registerAsBinding();
815
+ if (input.tagName === 'SELECT') {
816
+ observeSelectMutations(input, () => {
817
+ if (!updateMode) {
818
+ return;
819
+ }
820
+ input.value = parent.state().controlValue();
821
+ }, parent.destroyRef);
822
+ }
823
+ const bindings = createBindings();
824
+ return () => {
825
+ const state = parent.state();
826
+ const controlValue = state.controlValue();
827
+ if (bindingUpdated(bindings, 'controlValue', controlValue)) {
828
+ setNativeControlValue(input, controlValue);
838
829
  }
839
- if (!pattern.test(ctx.value())) {
840
- if (config?.error) {
841
- return getOption(config.error, ctx);
842
- } else {
843
- return patternError(pattern, {
844
- message: getOption(config?.message, ctx)
845
- });
830
+ for (const name of CONTROL_BINDING_NAMES) {
831
+ const value = readFieldStateBindingValue(state, name);
832
+ if (bindingUpdated(bindings, name, value)) {
833
+ host.setInputOnDirectives(name, value);
834
+ if (parent.elementAcceptsNativeProperty(name)) {
835
+ setNativeDomProperty(parent.renderer, input, name, value);
836
+ }
846
837
  }
847
838
  }
848
- return undefined;
849
- });
839
+ updateMode = true;
840
+ };
850
841
  }
851
842
 
852
- function required(path, config) {
853
- const REQUIRED_MEMO = metadata(path, createMetadataKey(), ctx => config?.when ? config.when(ctx) : true);
854
- metadata(path, REQUIRED, ({
855
- state
856
- }) => state.metadata(REQUIRED_MEMO)());
857
- validate(path, ctx => {
858
- if (ctx.state.metadata(REQUIRED_MEMO)() && isEmpty(ctx.value())) {
859
- if (config?.error) {
860
- return getOption(config.error, ctx);
861
- } else {
862
- return requiredError({
863
- message: getOption(config?.message, ctx)
864
- });
843
+ const ɵNgFieldDirective = Symbol();
844
+ const FORM_FIELD = new InjectionToken(typeof ngDevMode !== 'undefined' && ngDevMode ? 'FORM_FIELD' : '');
845
+ class FormField {
846
+ fieldTree = input.required({
847
+ ...(ngDevMode ? {
848
+ debugName: "fieldTree"
849
+ } : {}),
850
+ alias: 'formField'
851
+ });
852
+ renderer = inject(Renderer2);
853
+ destroyRef = inject(DestroyRef);
854
+ state = computed(() => this.fieldTree()(), ...(ngDevMode ? [{
855
+ debugName: "state"
856
+ }] : []));
857
+ injector = inject(Injector);
858
+ element = inject(ElementRef).nativeElement;
859
+ elementIsNativeFormElement = isNativeFormElement(this.element);
860
+ elementAcceptsNumericValues = isNumericFormElement(this.element);
861
+ elementAcceptsTextualValues = isTextualFormElement(this.element);
862
+ nativeFormElement = this.elementIsNativeFormElement ? this.element : undefined;
863
+ focuser = options => this.element.focus(options);
864
+ controlValueAccessors = inject(NG_VALUE_ACCESSOR, {
865
+ optional: true,
866
+ self: true
867
+ });
868
+ config = inject(SIGNAL_FORMS_CONFIG, {
869
+ optional: true
870
+ });
871
+ parseErrorsSource = signal(undefined, ...(ngDevMode ? [{
872
+ debugName: "parseErrorsSource"
873
+ }] : []));
874
+ _interopNgControl;
875
+ get interopNgControl() {
876
+ return this._interopNgControl ??= new InteropNgControl(this.state);
877
+ }
878
+ parseErrors = computed(() => this.parseErrorsSource()?.().map(err => ({
879
+ ...err,
880
+ fieldTree: untracked(this.fieldTree),
881
+ formField: this
882
+ })) ?? [], ...(ngDevMode ? [{
883
+ debugName: "parseErrors"
884
+ }] : []));
885
+ errors = computed(() => this.state().errors().filter(err => !err.formField || err.formField === this), ...(ngDevMode ? [{
886
+ debugName: "errors"
887
+ }] : []));
888
+ isFieldBinding = false;
889
+ get controlValueAccessor() {
890
+ return this.controlValueAccessors?.[0] ?? this.interopNgControl?.valueAccessor ?? undefined;
891
+ }
892
+ installClassBindingEffect() {
893
+ const classes = Object.entries(this.config?.classes ?? {}).map(([className, computation]) => [className, computed(() => computation(this))]);
894
+ if (classes.length === 0) {
895
+ return;
896
+ }
897
+ const bindings = createBindings();
898
+ afterRenderEffect({
899
+ write: () => {
900
+ for (const [className, computation] of classes) {
901
+ const active = computation();
902
+ if (bindingUpdated(bindings, className, active)) {
903
+ if (active) {
904
+ this.renderer.addClass(this.element, className);
905
+ } else {
906
+ this.renderer.removeClass(this.element, className);
907
+ }
908
+ }
909
+ }
865
910
  }
911
+ }, {
912
+ injector: this.injector
913
+ });
914
+ }
915
+ focus(options) {
916
+ this.focuser(options);
917
+ }
918
+ registerAsBinding(bindingOptions) {
919
+ if (this.isFieldBinding) {
920
+ throw new _RuntimeError(1913, ngDevMode && 'FormField already registered as a binding');
921
+ }
922
+ this.isFieldBinding = true;
923
+ this.installClassBindingEffect();
924
+ if (bindingOptions?.focus) {
925
+ this.focuser = bindingOptions.focus;
926
+ }
927
+ effect(onCleanup => {
928
+ const fieldNode = this.state();
929
+ fieldNode.nodeState.formFieldBindings.update(controls => [...controls, this]);
930
+ onCleanup(() => {
931
+ fieldNode.nodeState.formFieldBindings.update(controls => controls.filter(c => c !== this));
932
+ });
933
+ }, {
934
+ injector: this.injector
935
+ });
936
+ }
937
+ [ɵNgFieldDirective];
938
+ ɵngControlCreate(host) {
939
+ if (host.hasPassThrough) {
940
+ return;
866
941
  }
867
- return undefined;
868
- });
869
- }
870
-
871
- function validateAsync(path, opts) {
872
- assertPathIsCurrent(path);
873
- const pathNode = FieldPathNode.unwrapFieldPath(path);
874
- const RESOURCE = createManagedMetadataKey(opts.factory);
875
- metadata(path, RESOURCE, ctx => {
876
- const node = ctx.stateOf(path);
877
- const validationState = node.validationState;
878
- if (validationState.shouldSkipValidation() || !validationState.syncValid()) {
879
- return undefined;
942
+ if (this.controlValueAccessor) {
943
+ this.ɵngControlUpdate = cvaControlCreate(host, this);
944
+ } else if (host.customControl) {
945
+ this.ɵngControlUpdate = customControlCreate(host, this);
946
+ } else if (this.elementIsNativeFormElement) {
947
+ this.ɵngControlUpdate = nativeControlCreate(host, this);
948
+ } else {
949
+ throw new _RuntimeError(1914, 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.`);
880
950
  }
881
- return opts.params(ctx);
882
- });
883
- pathNode.builder.addAsyncErrorRule(ctx => {
884
- const res = ctx.state.metadata(RESOURCE);
885
- let errors;
886
- switch (res.status()) {
887
- case 'idle':
888
- return undefined;
889
- case 'loading':
890
- case 'reloading':
891
- return 'pending';
892
- case 'resolved':
893
- case 'local':
894
- if (!res.hasValue()) {
895
- return undefined;
896
- }
897
- errors = opts.onSuccess(res.value(), ctx);
898
- return addDefaultField(errors, ctx.fieldTree);
899
- case 'error':
900
- errors = opts.onError(res.error(), ctx);
901
- return addDefaultField(errors, ctx.fieldTree);
951
+ }
952
+ ɵngControlUpdate;
953
+ elementAcceptsNativeProperty(key) {
954
+ if (!this.elementIsNativeFormElement) {
955
+ return false;
902
956
  }
903
- });
904
- }
905
-
906
- function validateTree(path, logic) {
907
- assertPathIsCurrent(path);
908
- const pathNode = FieldPathNode.unwrapFieldPath(path);
909
- pathNode.builder.addSyncTreeErrorRule(ctx => addDefaultField(logic(ctx), ctx.fieldTree));
910
- }
911
-
912
- function validateStandardSchema(path, schema) {
913
- const VALIDATOR_MEMO = metadata(path, createMetadataKey(), ({
914
- value
915
- }) => {
916
- return schema['~standard'].validate(value());
917
- });
918
- validateTree(path, ({
919
- state,
920
- fieldTreeOf
921
- }) => {
922
- const result = state.metadata(VALIDATOR_MEMO)();
923
- if (_isPromise(result)) {
924
- return [];
957
+ switch (key) {
958
+ case 'min':
959
+ case 'max':
960
+ return this.elementAcceptsNumericValues;
961
+ case 'minLength':
962
+ case 'maxLength':
963
+ return this.elementAcceptsTextualValues;
964
+ case 'disabled':
965
+ case 'required':
966
+ case 'readonly':
967
+ case 'name':
968
+ return true;
969
+ default:
970
+ return false;
925
971
  }
926
- return result?.issues?.map(issue => standardIssueToFormTreeError(fieldTreeOf(path), issue)) ?? [];
972
+ }
973
+ static ɵfac = i0.ɵɵngDeclareFactory({
974
+ minVersion: "12.0.0",
975
+ version: "21.2.0-next.3",
976
+ ngImport: i0,
977
+ type: FormField,
978
+ deps: [],
979
+ target: i0.ɵɵFactoryTarget.Directive
927
980
  });
928
- validateAsync(path, {
929
- params: ({
930
- state
931
- }) => {
932
- const result = state.metadata(VALIDATOR_MEMO)();
933
- return _isPromise(result) ? result : undefined;
934
- },
935
- factory: params => {
936
- return resource({
937
- params,
938
- loader: async ({
939
- params
940
- }) => (await params)?.issues ?? []
941
- });
981
+ static ɵdir = i0.ɵɵngDeclareDirective({
982
+ minVersion: "17.1.0",
983
+ version: "21.2.0-next.3",
984
+ type: FormField,
985
+ isStandalone: true,
986
+ selector: "[formField]",
987
+ inputs: {
988
+ fieldTree: {
989
+ classPropertyName: "fieldTree",
990
+ publicName: "formField",
991
+ isSignal: true,
992
+ isRequired: true,
993
+ transformFunction: null
994
+ }
942
995
  },
943
- onSuccess: (issues, {
944
- fieldTreeOf
945
- }) => {
946
- return issues.map(issue => standardIssueToFormTreeError(fieldTreeOf(path), issue));
996
+ providers: [{
997
+ provide: FORM_FIELD,
998
+ useExisting: FormField
999
+ }, {
1000
+ provide: NgControl,
1001
+ useFactory: () => inject(FormField).interopNgControl
1002
+ }, {
1003
+ provide: FORM_FIELD_PARSE_ERRORS,
1004
+ useFactory: () => inject(FormField).parseErrorsSource
1005
+ }],
1006
+ exportAs: ["formField"],
1007
+ controlCreate: {
1008
+ passThroughInput: "formField"
947
1009
  },
948
- onError: () => {}
1010
+ ngImport: i0
949
1011
  });
950
1012
  }
951
- function standardSchemaError(issue, options) {
952
- return new StandardSchemaValidationError(issue, options);
953
- }
954
- function standardIssueToFormTreeError(fieldTree, issue) {
955
- let target = fieldTree;
956
- for (const pathPart of issue.path ?? []) {
957
- const pathKey = typeof pathPart === 'object' ? pathPart.key : pathPart;
958
- target = target[pathKey];
959
- }
960
- return addDefaultField(standardSchemaError(issue, {
961
- message: issue.message
962
- }), target);
963
- }
964
- class StandardSchemaValidationError extends BaseNgValidationError {
965
- issue;
966
- kind = 'standardSchema';
967
- constructor(issue, options) {
968
- super(options);
969
- this.issue = issue;
1013
+ i0.ɵɵngDeclareClassMetadata({
1014
+ minVersion: "12.0.0",
1015
+ version: "21.2.0-next.3",
1016
+ ngImport: i0,
1017
+ type: FormField,
1018
+ decorators: [{
1019
+ type: Directive,
1020
+ args: [{
1021
+ selector: '[formField]',
1022
+ exportAs: 'formField',
1023
+ providers: [{
1024
+ provide: FORM_FIELD,
1025
+ useExisting: FormField
1026
+ }, {
1027
+ provide: NgControl,
1028
+ useFactory: () => inject(FormField).interopNgControl
1029
+ }, {
1030
+ provide: FORM_FIELD_PARSE_ERRORS,
1031
+ useFactory: () => inject(FormField).parseErrorsSource
1032
+ }]
1033
+ }]
1034
+ }],
1035
+ propDecorators: {
1036
+ fieldTree: [{
1037
+ type: i0.Input,
1038
+ args: [{
1039
+ isSignal: true,
1040
+ alias: "formField",
1041
+ required: true
1042
+ }]
1043
+ }]
970
1044
  }
971
- }
1045
+ });
972
1046
 
973
- function validateHttp(path, opts) {
974
- validateAsync(path, {
975
- params: opts.request,
976
- factory: request => httpResource(request, opts.options),
977
- onSuccess: opts.onSuccess,
978
- onError: opts.onError
1047
+ class FormRoot {
1048
+ fieldTree = input.required({
1049
+ ...(ngDevMode ? {
1050
+ debugName: "fieldTree"
1051
+ } : {}),
1052
+ alias: 'formRoot'
1053
+ });
1054
+ onSubmit(event) {
1055
+ event.preventDefault();
1056
+ submit(this.fieldTree());
1057
+ }
1058
+ static ɵfac = i0.ɵɵngDeclareFactory({
1059
+ minVersion: "12.0.0",
1060
+ version: "21.2.0-next.3",
1061
+ ngImport: i0,
1062
+ type: FormRoot,
1063
+ deps: [],
1064
+ target: i0.ɵɵFactoryTarget.Directive
1065
+ });
1066
+ static ɵdir = i0.ɵɵngDeclareDirective({
1067
+ minVersion: "17.1.0",
1068
+ version: "21.2.0-next.3",
1069
+ type: FormRoot,
1070
+ isStandalone: true,
1071
+ selector: "form[formRoot]",
1072
+ inputs: {
1073
+ fieldTree: {
1074
+ classPropertyName: "fieldTree",
1075
+ publicName: "formRoot",
1076
+ isSignal: true,
1077
+ isRequired: true,
1078
+ transformFunction: null
1079
+ }
1080
+ },
1081
+ host: {
1082
+ attributes: {
1083
+ "novalidate": ""
1084
+ },
1085
+ listeners: {
1086
+ "submit": "onSubmit($event)"
1087
+ }
1088
+ },
1089
+ ngImport: i0
979
1090
  });
980
1091
  }
1092
+ i0.ɵɵngDeclareClassMetadata({
1093
+ minVersion: "12.0.0",
1094
+ version: "21.2.0-next.3",
1095
+ ngImport: i0,
1096
+ type: FormRoot,
1097
+ decorators: [{
1098
+ type: Directive,
1099
+ args: [{
1100
+ selector: 'form[formRoot]',
1101
+ host: {
1102
+ 'novalidate': '',
1103
+ '(submit)': 'onSubmit($event)'
1104
+ }
1105
+ }]
1106
+ }],
1107
+ propDecorators: {
1108
+ fieldTree: [{
1109
+ type: i0.Input,
1110
+ args: [{
1111
+ isSignal: true,
1112
+ alias: "formRoot",
1113
+ required: true
1114
+ }]
1115
+ }]
1116
+ }
1117
+ });
981
1118
 
982
- function debounce(path, durationOrDebouncer) {
983
- assertPathIsCurrent(path);
984
- const pathNode = FieldPathNode.unwrapFieldPath(path);
985
- const debouncer = typeof durationOrDebouncer === 'function' ? durationOrDebouncer : durationOrDebouncer > 0 ? debounceForDuration(durationOrDebouncer) : immediate;
986
- pathNode.builder.addMetadataRule(DEBOUNCER, () => debouncer);
987
- }
988
- function debounceForDuration(durationInMilliseconds) {
989
- return (_context, abortSignal) => {
990
- return new Promise(resolve => {
991
- let timeoutId;
992
- const onAbort = () => {
993
- clearTimeout(timeoutId);
994
- resolve();
995
- };
996
- timeoutId = setTimeout(() => {
997
- abortSignal.removeEventListener('abort', onAbort);
998
- resolve();
999
- }, durationInMilliseconds);
1000
- abortSignal.addEventListener('abort', onAbort, {
1001
- once: true
1002
- });
1003
- });
1004
- };
1005
- }
1006
- function immediate() {}
1007
-
1008
- export { BaseNgValidationError, EmailValidationError, FORM_FIELD, FormField, MAX, MAX_LENGTH, MIN, MIN_LENGTH, MaxLengthValidationError, MaxValidationError, MinLengthValidationError, MinValidationError, 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, validate, validateAsync, validateHttp, validateStandardSchema, validateTree, ɵNgFieldDirective };
1119
+ export { BaseNgValidationError, EmailValidationError, FORM_FIELD, FormField, FormRoot, MAX, MAX_LENGTH, MIN, MIN_LENGTH, MaxLengthValidationError, MaxValidationError, MinLengthValidationError, MinValidationError, 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 };
1009
1120
  //# sourceMappingURL=signals.mjs.map