@ng-forge/dynamic-forms 1.1.0 → 1.2.0-next.10
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/README.md +11 -11
- package/fesm2022/{ng-forge-dynamic-forms-array-field.component-CvjDqYyq.mjs → ng-forge-dynamic-forms-array-field.component-CSCKq_1K.mjs} +9 -13
- package/fesm2022/ng-forge-dynamic-forms-array-field.component-CSCKq_1K.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-CcoUnroD.mjs → ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-CTOTwzzw.mjs} +29 -7
- package/fesm2022/ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-CTOTwzzw.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-bootstrap-web-mcp-BtjqeJOj.mjs +1054 -0
- package/fesm2022/ng-forge-dynamic-forms-bootstrap-web-mcp-BtjqeJOj.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-container-field.component-DZfVWjiM.mjs → ng-forge-dynamic-forms-container-field.component-Dc3ROhHL.mjs} +14 -17
- package/fesm2022/ng-forge-dynamic-forms-container-field.component-Dc3ROhHL.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-css-wrapper.component-D_yRuLpu.mjs → ng-forge-dynamic-forms-css-wrapper.component-BzgX8lAD.mjs} +5 -5
- package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-BzgX8lAD.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-debounce-C9F4v1Ke.mjs +17 -0
- package/fesm2022/ng-forge-dynamic-forms-debounce-C9F4v1Ke.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-field-errors-wrapper.component-CIKbWqYA.mjs +25 -0
- package/fesm2022/ng-forge-dynamic-forms-field-errors-wrapper.component-CIKbWqYA.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-form-schema.service-Dx5Dlb15.mjs +24 -0
- package/fesm2022/ng-forge-dynamic-forms-form-schema.service-Dx5Dlb15.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-group-field.component-CPFgu3pM.mjs → ng-forge-dynamic-forms-group-field.component-DAC3P9DC.mjs} +9 -13
- package/fesm2022/ng-forge-dynamic-forms-group-field.component-DAC3P9DC.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-integration.mjs +49 -107
- package/fesm2022/ng-forge-dynamic-forms-integration.mjs.map +1 -1
- package/fesm2022/ng-forge-dynamic-forms-internal.mjs +434 -139
- package/fesm2022/ng-forge-dynamic-forms-internal.mjs.map +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-ng-forge-dynamic-forms-Du3V5AOb.mjs → ng-forge-dynamic-forms-ng-forge-dynamic-forms-CXQe_5td.mjs} +1930 -1847
- package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-CXQe_5td.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-page-field.component-amWhCdHL.mjs → ng-forge-dynamic-forms-page-field.component-DPKJcwUO.mjs} +94 -15
- package/fesm2022/ng-forge-dynamic-forms-page-field.component-DPKJcwUO.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-row-wrapper.component-BBWmqQA_.mjs → ng-forge-dynamic-forms-row-wrapper.component-DMEeKZny.mjs} +5 -5
- package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-DMEeKZny.mjs.map +1 -0
- package/fesm2022/ng-forge-dynamic-forms-schema-builder-BvDTzfdM.mjs +570 -0
- package/fesm2022/ng-forge-dynamic-forms-schema-builder-BvDTzfdM.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-text-field.component-C-f80sbk.mjs → ng-forge-dynamic-forms-text-field.component-BB-jutBq.mjs} +4 -4
- package/fesm2022/{ng-forge-dynamic-forms-text-field.component-C-f80sbk.mjs.map → ng-forge-dynamic-forms-text-field.component-BB-jutBq.mjs.map} +1 -1
- package/fesm2022/ng-forge-dynamic-forms.mjs +2 -2
- package/package.json +2 -2
- package/types/ng-forge-dynamic-forms-integration.d.ts +15 -33
- package/types/ng-forge-dynamic-forms-internal.d.ts +705 -292
- package/types/ng-forge-dynamic-forms.d.ts +400 -46
- package/fesm2022/ng-forge-dynamic-forms-array-field.component-CvjDqYyq.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-CcoUnroD.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-container-field.component-DZfVWjiM.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-container-utils-BZFz6ztF.mjs +0 -24
- package/fesm2022/ng-forge-dynamic-forms-container-utils-BZFz6ztF.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-css-wrapper.component-D_yRuLpu.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-emit-initialization-DhHwU6FJ.mjs +0 -17
- package/fesm2022/ng-forge-dynamic-forms-emit-initialization-DhHwU6FJ.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-group-field.component-CPFgu3pM.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-Du3V5AOb.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-page-field.component-amWhCdHL.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-row-wrapper.component-BBWmqQA_.mjs.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as _ng_forge_dynamic_forms_internal from '@ng-forge/dynamic-forms/internal';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { Signal, Type, ResourceRef, ViewContainerRef, InjectionToken, WritableSignal, Injector, TemplateRef, PipeTransform, Resource } from '@angular/core';
|
|
3
|
+
import { Signal, Type, ResourceRef, ViewContainerRef, InjectionToken, WritableSignal, Injector, TemplateRef, PipeTransform, Resource, Provider } from '@angular/core';
|
|
4
4
|
import { Observable, OperatorFunction } from 'rxjs';
|
|
5
|
-
import { FieldContext,
|
|
5
|
+
import { FieldContext, ValidationError as ValidationError$1, TreeValidationResult, FieldTree, ReadonlyFieldState, LogicFn, SchemaPath, SchemaPathTree } from '@angular/forms/signals';
|
|
6
6
|
import { FormSchema } from '@ng-forge/dynamic-forms/schema';
|
|
7
7
|
|
|
8
8
|
type DynamicText = string | Observable<string> | Signal<string>;
|
|
@@ -83,6 +83,45 @@ interface FieldMeta {
|
|
|
83
83
|
[key: string]: string | number | boolean | undefined;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Per-field WebMCP exposure policy.
|
|
88
|
+
*
|
|
89
|
+
* WebMCP tools are callable by any agent that reaches the page, so what a field
|
|
90
|
+
* lets an agent read and write is a security decision, not a formatting one.
|
|
91
|
+
* Sensible defaults are derived from the field itself (see below); this
|
|
92
|
+
* overrides them where the author knows better.
|
|
93
|
+
*
|
|
94
|
+
* Defaults, applied when a key is left unset:
|
|
95
|
+
*
|
|
96
|
+
* | Field | `readable` | `writable` |
|
|
97
|
+
* | --- | --- | --- |
|
|
98
|
+
* | `type: 'hidden'` | `false` | `false` |
|
|
99
|
+
* | `props.type === 'password'` | `false` | `true` |
|
|
100
|
+
* | `readonly: true`, or a `derivation` | `true` | `false` |
|
|
101
|
+
* | anything else | `true` | `true` |
|
|
102
|
+
*
|
|
103
|
+
* @experimental
|
|
104
|
+
*/
|
|
105
|
+
interface FieldWebMcpOptions {
|
|
106
|
+
/**
|
|
107
|
+
* Whether an agent may read this field's value back in a tool response.
|
|
108
|
+
*
|
|
109
|
+
* A field that is not readable is still reported by name, so the agent knows
|
|
110
|
+
* it exists and whether it currently holds a value — only the value itself is
|
|
111
|
+
* withheld.
|
|
112
|
+
*/
|
|
113
|
+
readable?: boolean;
|
|
114
|
+
/** Whether an agent may write this field. A write to a non-writable field is rejected. */
|
|
115
|
+
writable?: boolean;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* A field's WebMCP exposure policy. `false` is shorthand for hiding the field
|
|
119
|
+
* from agents entirely (`{ readable: false, writable: false }`).
|
|
120
|
+
*
|
|
121
|
+
* @experimental
|
|
122
|
+
*/
|
|
123
|
+
type FieldWebMcpConfig = FieldWebMcpOptions | false;
|
|
124
|
+
|
|
86
125
|
/**
|
|
87
126
|
* Base interface for all dynamic form field definitions.
|
|
88
127
|
*
|
|
@@ -168,6 +207,17 @@ interface FieldDef<TProps, TMeta extends FieldMeta = FieldMeta> {
|
|
|
168
207
|
skipDefaultWrappers?: boolean;
|
|
169
208
|
/** Addons rendered inside this field's slots (typically `prefix` / `suffix`). */
|
|
170
209
|
addons?: ReadonlyArray<AnyAddon>;
|
|
210
|
+
/**
|
|
211
|
+
* What a browser AI agent may read and write on this field, when the form
|
|
212
|
+
* opts in through `options.webMcp`. Ignored otherwise.
|
|
213
|
+
*
|
|
214
|
+
* Defaults are derived from the field (a password input is not readable, a
|
|
215
|
+
* `readonly` or derived field is not writable, a `hidden` field type is
|
|
216
|
+
* neither); `false` hides the field from agents entirely.
|
|
217
|
+
*
|
|
218
|
+
* @experimental
|
|
219
|
+
*/
|
|
220
|
+
webMcp?: FieldWebMcpConfig;
|
|
171
221
|
}
|
|
172
222
|
type IncludedKeys = 'label' | 'className' | 'hidden' | 'tabIndex';
|
|
173
223
|
/**
|
|
@@ -480,12 +530,242 @@ interface RowField<TFields extends readonly RowAllowedChildren[] = readonly RowA
|
|
|
480
530
|
declare function isRowField(field: FieldDef<any>): field is RowField;
|
|
481
531
|
type RowComponent = FieldComponent<RowField<RowAllowedChildren[]>>;
|
|
482
532
|
|
|
533
|
+
/**
|
|
534
|
+
* Custom validator function signature using Angular's public FieldContext API
|
|
535
|
+
*
|
|
536
|
+
* @template TValue The type of value stored in the field being validated
|
|
537
|
+
*/
|
|
538
|
+
type CustomValidator<TValue = unknown> = (ctx: FieldContext<TValue>, params?: Record<string, unknown>) => ValidationError$1 | ValidationError$1[] | null;
|
|
539
|
+
/**
|
|
540
|
+
* Async custom validator configuration using Angular's resource-based API
|
|
541
|
+
*
|
|
542
|
+
* @template TValue The type of value stored in the field being validated
|
|
543
|
+
* @template TParams The type of params passed to the resource
|
|
544
|
+
* @template TResult The type of result returned by the resource
|
|
545
|
+
*/
|
|
546
|
+
interface AsyncCustomValidator<TValue = unknown, TParams = unknown, TResult = unknown> {
|
|
547
|
+
/**
|
|
548
|
+
* Function that receives field context and returns resource params.
|
|
549
|
+
* Params will be tracked as a signal and trigger resource reload when changed.
|
|
550
|
+
*/
|
|
551
|
+
readonly params: (ctx: FieldContext<TValue>, config?: Record<string, unknown>) => TParams;
|
|
552
|
+
/**
|
|
553
|
+
* Function that creates a ResourceRef from the params signal.
|
|
554
|
+
* The resource will be automatically managed by Angular's lifecycle.
|
|
555
|
+
*/
|
|
556
|
+
readonly factory: (params: Signal<TParams | undefined>) => ResourceRef<TResult | undefined>;
|
|
557
|
+
/**
|
|
558
|
+
* Optional function to map successful resource result to validation errors.
|
|
559
|
+
* Return null if validation passes, or ValidationError/ValidationError[] if validation fails.
|
|
560
|
+
*/
|
|
561
|
+
readonly onSuccess?: (result: TResult, ctx: FieldContext<TValue>) => TreeValidationResult;
|
|
562
|
+
/**
|
|
563
|
+
* Optional error handler for resource errors (network failures, etc.).
|
|
564
|
+
* Return null to ignore the error, or ValidationError to display it to the user.
|
|
565
|
+
*/
|
|
566
|
+
readonly onError?: (error: unknown, ctx: FieldContext<TValue>) => ValidationError$1 | ValidationError$1[] | null;
|
|
567
|
+
}
|
|
568
|
+
/** HTTP request configuration for validateHttp API */
|
|
569
|
+
interface HttpResourceRequest {
|
|
570
|
+
url: string;
|
|
571
|
+
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
572
|
+
body?: unknown;
|
|
573
|
+
headers?: Record<string, string | string[]>;
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* HTTP-based validator configuration for Angular's validateHttp API
|
|
577
|
+
*
|
|
578
|
+
* @template TValue The type of value stored in the field being validated
|
|
579
|
+
* @template TResult The type of HTTP response
|
|
580
|
+
*/
|
|
581
|
+
interface HttpCustomValidator<TValue = unknown, TResult = unknown> {
|
|
582
|
+
/**
|
|
583
|
+
* Function that returns the HTTP request configuration.
|
|
584
|
+
* Can return:
|
|
585
|
+
* - `undefined` to skip validation (e.g., if field is empty)
|
|
586
|
+
* - `string` for simple GET requests (just the URL)
|
|
587
|
+
* - `HttpResourceRequest` for full control (method, body, headers)
|
|
588
|
+
*/
|
|
589
|
+
readonly request: (ctx: FieldContext<TValue>, config?: Record<string, unknown>) => string | HttpResourceRequest | undefined;
|
|
590
|
+
/** REQUIRED function to map successful HTTP response to validation errors. */
|
|
591
|
+
readonly onSuccess: (result: TResult, ctx: FieldContext<TValue>) => TreeValidationResult;
|
|
592
|
+
/**
|
|
593
|
+
* Optional error handler for HTTP errors (network failures, 4xx/5xx status codes).
|
|
594
|
+
* Return null to ignore the error, or ValidationError to display it to the user.
|
|
595
|
+
*/
|
|
596
|
+
readonly onError?: (error: unknown, ctx: FieldContext<TValue>) => ValidationError$1 | ValidationError$1[] | null;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/** Mapping configuration for interpreting HTTP responses as validation results. */
|
|
600
|
+
interface HttpValidationResponseMapping {
|
|
601
|
+
/** Expression evaluated with scope `{ response }`. Must evaluate to `true` (strict boolean) to be valid. */
|
|
602
|
+
validWhen: string;
|
|
603
|
+
/** Error kind returned when validation fails — maps to field-level `validationMessages` */
|
|
604
|
+
errorKind: string;
|
|
605
|
+
/**
|
|
606
|
+
* Parameters to include in the validation error for message interpolation.
|
|
607
|
+
* Map of parameter names to expressions evaluated against `{ response }`.
|
|
608
|
+
*/
|
|
609
|
+
errorParams?: Record<string, string>;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/** Base configuration shared by all validators */
|
|
613
|
+
interface BaseValidatorConfig {
|
|
614
|
+
/** Conditional logic for when validator applies */
|
|
615
|
+
when?: ConditionalExpression;
|
|
616
|
+
}
|
|
617
|
+
/** Built-in validator configuration (required, email, min, max, etc.) */
|
|
618
|
+
interface BuiltInValidatorConfig extends BaseValidatorConfig {
|
|
619
|
+
/** Validator type identifier */
|
|
620
|
+
type: 'required' | 'email' | 'min' | 'max' | 'minLength' | 'maxLength' | 'pattern';
|
|
621
|
+
/** Static value for the validator (e.g., min value, pattern) */
|
|
622
|
+
value?: number | string | RegExp;
|
|
623
|
+
/** Dynamic value expression that evaluates to validator parameter */
|
|
624
|
+
expression?: string;
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Custom validator configuration using Angular's public FieldContext API.
|
|
628
|
+
* Returns ValidationError | ValidationError[] | null synchronously.
|
|
629
|
+
*/
|
|
630
|
+
interface CustomValidatorConfig extends BaseValidatorConfig {
|
|
631
|
+
/** Validator type identifier */
|
|
632
|
+
type: 'custom';
|
|
633
|
+
/** Name of registered validator function (function-based pattern) */
|
|
634
|
+
functionName?: string;
|
|
635
|
+
/**
|
|
636
|
+
* Inline custom validator (code-only authoring).
|
|
637
|
+
* Mutually exclusive with `functionName`. NOT JSON-serializable.
|
|
638
|
+
*/
|
|
639
|
+
fn?: CustomValidator;
|
|
640
|
+
/** Optional parameters to pass to validator function */
|
|
641
|
+
params?: Record<string, unknown>;
|
|
642
|
+
/** JavaScript expression to evaluate (expression-based pattern) */
|
|
643
|
+
expression?: string;
|
|
644
|
+
/** Error kind for expression-based validators - links to validationMessages */
|
|
645
|
+
kind?: string;
|
|
646
|
+
/**
|
|
647
|
+
* Parameters to include in error object for message interpolation (expression-based pattern)
|
|
648
|
+
* Map of parameter names to expressions that will be evaluated in the same context as the validation expression
|
|
649
|
+
*/
|
|
650
|
+
errorParams?: Record<string, string>;
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Shared fields for both branches of {@link AsyncValidatorConfig}.
|
|
654
|
+
*
|
|
655
|
+
* @internal
|
|
656
|
+
*/
|
|
657
|
+
interface AsyncValidatorConfigShared extends BaseValidatorConfig {
|
|
658
|
+
/** Validator type identifier. */
|
|
659
|
+
type: 'async';
|
|
660
|
+
/** Optional parameters to pass to validator function */
|
|
661
|
+
params?: Record<string, unknown>;
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Async custom validator configuration using Angular's `validateAsync` API.
|
|
665
|
+
* Returns Observable<ValidationError | ValidationError[] | null>.
|
|
666
|
+
*/
|
|
667
|
+
type AsyncValidatorConfig = (AsyncValidatorConfigShared & {
|
|
668
|
+
/** Name of registered async validator function */
|
|
669
|
+
functionName: string;
|
|
670
|
+
/** Inline form is forbidden when `functionName` is set */
|
|
671
|
+
fn?: never;
|
|
672
|
+
}) | (AsyncValidatorConfigShared & {
|
|
673
|
+
/** Inline async validator. Mutually exclusive with `functionName`. */
|
|
674
|
+
fn: AsyncCustomValidator;
|
|
675
|
+
/** Registered form is forbidden when `fn` is set */
|
|
676
|
+
functionName?: never;
|
|
677
|
+
});
|
|
678
|
+
/**
|
|
679
|
+
* Shared fields for both branches of {@link FunctionHttpValidatorConfig}.
|
|
680
|
+
*
|
|
681
|
+
* @internal
|
|
682
|
+
*/
|
|
683
|
+
interface FunctionHttpValidatorConfigShared extends BaseValidatorConfig {
|
|
684
|
+
/** Validator type identifier. */
|
|
685
|
+
type: 'http';
|
|
686
|
+
/** Optional parameters to pass to HTTP validator */
|
|
687
|
+
params?: Record<string, unknown>;
|
|
688
|
+
}
|
|
689
|
+
/** Function-based HTTP validator configuration. Uses Angular's `validateHttp` API. */
|
|
690
|
+
type FunctionHttpValidatorConfig = (FunctionHttpValidatorConfigShared & {
|
|
691
|
+
/** Name of registered HTTP validator configuration */
|
|
692
|
+
functionName: string;
|
|
693
|
+
/** Inline form is forbidden when `functionName` is set */
|
|
694
|
+
fn?: never;
|
|
695
|
+
}) | (FunctionHttpValidatorConfigShared & {
|
|
696
|
+
/** Inline HTTP validator. Mutually exclusive with `functionName`. */
|
|
697
|
+
fn: HttpCustomValidator;
|
|
698
|
+
/** Registered form is forbidden when `fn` is set */
|
|
699
|
+
functionName?: never;
|
|
700
|
+
});
|
|
701
|
+
/** Declarative HTTP validator configuration — fully JSON-serializable, no function registration required. */
|
|
702
|
+
interface DeclarativeHttpValidatorConfig extends BaseValidatorConfig {
|
|
703
|
+
/** Validator type identifier */
|
|
704
|
+
type: 'http';
|
|
705
|
+
/** HTTP request configuration with expression-based query params and body */
|
|
706
|
+
http: HttpRequestConfig;
|
|
707
|
+
/** Mapping that interprets the HTTP response as a validation result */
|
|
708
|
+
responseMapping: HttpValidationResponseMapping;
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Configuration for signal forms validator functions that can be serialized from API.
|
|
712
|
+
* Discriminated union type for type-safe validator configuration.
|
|
713
|
+
*/
|
|
714
|
+
type ValidatorConfig = BuiltInValidatorConfig | CustomValidatorConfig | AsyncValidatorConfig | FunctionHttpValidatorConfig | DeclarativeHttpValidatorConfig;
|
|
715
|
+
/** Type guard to distinguish function-based HTTP validators from declarative ones. */
|
|
716
|
+
declare function isFunctionHttpValidator(config: FunctionHttpValidatorConfig | DeclarativeHttpValidatorConfig): config is FunctionHttpValidatorConfig;
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* Re-export Angular's ValidationError for consistency
|
|
720
|
+
* This replaces our custom ValidationError interface
|
|
721
|
+
*/
|
|
722
|
+
type ValidationError = ValidationError$1;
|
|
723
|
+
/**
|
|
724
|
+
* Resolves a validation message from the validation error, so error params
|
|
725
|
+
* (e.g. maxLength, min) can be passed to an i18n layer natively.
|
|
726
|
+
*/
|
|
727
|
+
type ValidationMessageResolver = (error: ValidationError) => DynamicText;
|
|
728
|
+
/** A validation message: static or reactive text, or a function of the validation error. */
|
|
729
|
+
type ValidationMessage = DynamicText | ValidationMessageResolver;
|
|
730
|
+
interface ValidationMessages {
|
|
731
|
+
required?: ValidationMessage;
|
|
732
|
+
email?: ValidationMessage;
|
|
733
|
+
min?: ValidationMessage;
|
|
734
|
+
max?: ValidationMessage;
|
|
735
|
+
minLength?: ValidationMessage;
|
|
736
|
+
maxLength?: ValidationMessage;
|
|
737
|
+
pattern?: ValidationMessage;
|
|
738
|
+
[key: string]: ValidationMessage | undefined;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/**
|
|
742
|
+
* Tree-level validation for container fields (`group` / `array`).
|
|
743
|
+
*
|
|
744
|
+
* A validator here runs against the container's own subtree: `ctx.value()` is the group's
|
|
745
|
+
* object or the array's item list. Layout containers (`page`, `row`, `container`) flatten
|
|
746
|
+
* into their parent and have no schema path, so they are excluded.
|
|
747
|
+
*/
|
|
748
|
+
interface ContainerValidation {
|
|
749
|
+
/**
|
|
750
|
+
* Marks every descendant required. An inherited default, not a rule on the container —
|
|
751
|
+
* a descendant's own `required` wins, including `false`. Use `minLength` for array size.
|
|
752
|
+
*/
|
|
753
|
+
readonly required?: boolean;
|
|
754
|
+
/** Validators applied to the container's own schema path. Gated by `validateWhenHidden`. */
|
|
755
|
+
readonly validators?: ValidatorConfig[];
|
|
756
|
+
/**
|
|
757
|
+
* Messages keyed by error `kind`. Rendered by the `field-errors` wrapper, attached
|
|
758
|
+
* automatically when the container declares `validators`.
|
|
759
|
+
*/
|
|
760
|
+
readonly validationMessages?: ValidationMessages;
|
|
761
|
+
}
|
|
762
|
+
|
|
483
763
|
/**
|
|
484
764
|
* Group field interface for creating logical field groupings that map to object values
|
|
485
765
|
* Groups create nested form structures where child field values are collected into an object
|
|
486
766
|
* This is a programmatic grouping only - users cannot customize this field type
|
|
487
767
|
*/
|
|
488
|
-
interface GroupField<TFields extends readonly GroupAllowedChildren[] = readonly GroupAllowedChildren[]> extends FieldDef<never
|
|
768
|
+
interface GroupField<TFields extends readonly GroupAllowedChildren[] = readonly GroupAllowedChildren[]> extends FieldDef<never>, ContainerValidation {
|
|
489
769
|
type: 'group';
|
|
490
770
|
readonly fields: TFields;
|
|
491
771
|
/** Groups do not have a label property - they are logical containers only * */
|
|
@@ -573,7 +853,7 @@ type ArrayItemTemplate = readonly ArrayAllowedChildren[];
|
|
|
573
853
|
*/
|
|
574
854
|
type ArrayItemDefinition = ArrayAllowedChildren | ArrayItemTemplate;
|
|
575
855
|
/** Array field interface for creating dynamic field collections that map to array values. */
|
|
576
|
-
interface ArrayField<TFields extends readonly ArrayItemDefinition[] = readonly ArrayItemDefinition[]> extends FieldDef<never
|
|
856
|
+
interface ArrayField<TFields extends readonly ArrayItemDefinition[] = readonly ArrayItemDefinition[]> extends FieldDef<never>, ContainerValidation {
|
|
577
857
|
type: 'array';
|
|
578
858
|
/**
|
|
579
859
|
* Array of item definitions. Each element defines one array item:
|
|
@@ -619,7 +899,7 @@ interface ArrayButtonConfig {
|
|
|
619
899
|
readonly props?: Record<string, unknown>;
|
|
620
900
|
}
|
|
621
901
|
/** Simplified array field interface for common use cases. */
|
|
622
|
-
interface SimplifiedArrayField extends FieldDef<never
|
|
902
|
+
interface SimplifiedArrayField extends FieldDef<never>, ContainerValidation {
|
|
623
903
|
type: 'array';
|
|
624
904
|
/**
|
|
625
905
|
* Template defining the structure of a single array item.
|
|
@@ -817,260 +1097,75 @@ type InferArrayValue<T, D extends number> = T extends {
|
|
|
817
1097
|
template: infer TArr;
|
|
818
1098
|
} ? TArr extends readonly RegisteredFieldTypes[] ? InferFormValueWithDepth<[...TArr], Depth[D]>[] : InferSingleTemplateValue<TArr, D>[] : T extends {
|
|
819
1099
|
fields: infer F;
|
|
820
|
-
} ? InferContainerChildren<F, Depth[D], unknown>[] : unknown[];
|
|
821
|
-
/**
|
|
822
|
-
* Recurse into a container's child fields, but only when they form a fixed-length
|
|
823
|
-
* tuple — i.e. a config authored with `as const`. A general `RegisteredFieldTypes[]`
|
|
824
|
-
* array (notably the base default `InferFormValue<RegisteredFieldTypes[]>` over the
|
|
825
|
-
* whole registry) bails to `TFallback`; recursing there would make the type explode
|
|
826
|
-
* past TypeScript's union-complexity limit (TS2590) and break consumers of the
|
|
827
|
-
* abstract `FormConfig`. `[...F]` re-mutabilises the readonly tuple for the inner
|
|
828
|
-
* `InferFormValueWithDepth` constraint.
|
|
829
|
-
*/
|
|
830
|
-
type InferContainerChildren<F, D extends number, TFallback> = F extends readonly RegisteredFieldTypes[] ? number extends F['length'] ? TFallback : InferFormValueWithDepth<[...F], D> : TFallback;
|
|
831
|
-
/** Process a single field and determine its contribution to the form value type */
|
|
832
|
-
type ProcessField<T, D extends number = 5> = [D] extends [never] ? Record<string, unknown> : T extends {
|
|
833
|
-
type: 'page' | 'row';
|
|
834
|
-
fields: infer F;
|
|
835
|
-
} ? InferContainerChildren<F, Depth[D], never> : T extends {
|
|
836
|
-
type: 'group';
|
|
837
|
-
key: infer K;
|
|
838
|
-
fields: infer F;
|
|
839
|
-
} ? K extends string ? {
|
|
840
|
-
[P in K]: InferContainerChildren<F, Depth[D], Record<string, unknown>>;
|
|
841
|
-
} : never : T extends {
|
|
842
|
-
type: 'array';
|
|
843
|
-
key: infer K;
|
|
844
|
-
} ? K extends string ? {
|
|
845
|
-
[P in K]: InferArrayValue<T, D>;
|
|
846
|
-
} : never : T extends {
|
|
847
|
-
type: 'text';
|
|
848
|
-
} ? never : T extends {
|
|
849
|
-
type: 'submit' | 'button' | 'next' | 'previous' | 'addArrayItem' | 'add-array-item' | 'prependArrayItem' | 'prepend-array-item' | 'insertArrayItem' | 'insert-array-item' | 'removeArrayItem' | 'remove-array-item' | 'popArrayItem' | 'pop-array-item' | 'shiftArrayItem' | 'shift-array-item';
|
|
850
|
-
} ? never : T extends {
|
|
851
|
-
key: infer K;
|
|
852
|
-
value: infer V;
|
|
853
|
-
} ? K extends string ? {
|
|
854
|
-
[P in K]: MaybeOptional<T, InferValueType<T, V>>;
|
|
855
|
-
} : never : T extends {
|
|
856
|
-
key: infer K;
|
|
857
|
-
} ? K extends string ? {
|
|
858
|
-
[P in K]: MaybeOptional<T, T extends {
|
|
859
|
-
nullable: true;
|
|
860
|
-
} ? string | null : string>;
|
|
861
|
-
} : never : never;
|
|
862
|
-
/** Internal helper with depth tracking */
|
|
863
|
-
type InferFormValueWithDepth<T extends RegisteredFieldTypes[], D extends number = 5> = UnionToIntersection<ProcessField<T[number], D>>;
|
|
864
|
-
/** Helper to extract fields from either fields array or FormConfig */
|
|
865
|
-
type ExtractFields<T> = T extends {
|
|
866
|
-
fields: infer TFields;
|
|
867
|
-
} ? TFields extends readonly RegisteredFieldTypes[] ? TFields : never : T extends readonly RegisteredFieldTypes[] ? T : never;
|
|
868
|
-
/**
|
|
869
|
-
* Infer form value type from fields array or FormConfig.
|
|
870
|
-
* Recursively processes nested structures and merges the results.
|
|
871
|
-
* Limited to 5 levels of nesting to prevent infinite type instantiation.
|
|
872
|
-
*/
|
|
873
|
-
type InferFormValue<T> = ExtractFields<T> extends readonly RegisteredFieldTypes[] ? InferFormValueWithDepth<ExtractFields<T> extends RegisteredFieldTypes[] ? ExtractFields<T> : [...ExtractFields<T>], 5> : never;
|
|
874
|
-
/**
|
|
875
|
-
* Default `TModel` for `DynamicForm` / `FormStateManager` generics.
|
|
876
|
-
*
|
|
877
|
-
* Equals `InferFormValue<T>` for any concrete config (an object type). Falls back
|
|
878
|
-
* to `Record<string, unknown>` only when inference yields a non-record — e.g. an
|
|
879
|
-
* empty `fields` array, where `InferFormValue` is `unknown` — so the `TModel
|
|
880
|
-
* extends Record<string, unknown>` bound always holds by construction.
|
|
881
|
-
*
|
|
882
|
-
* Crucially, unlike the older `InferFormValue<T> & Record<string, unknown>`
|
|
883
|
-
* default, this does NOT graft an index signature onto the model. That matters at
|
|
884
|
-
* the consumer call site: the index signature made `value['anyTypo']` resolve to
|
|
885
|
-
* `unknown` instead of erroring, silently defeating the typo-safety the inference
|
|
886
|
-
* is meant to provide.
|
|
887
|
-
*/
|
|
888
|
-
type InferFormModel<T, M = InferFormValue<T>> = M extends Record<string, unknown> ? M : Record<string, unknown>;
|
|
889
|
-
|
|
890
|
-
/**
|
|
891
|
-
* Custom validator function signature using Angular's public FieldContext API
|
|
892
|
-
*
|
|
893
|
-
* @template TValue The type of value stored in the field being validated
|
|
894
|
-
*/
|
|
895
|
-
type CustomValidator<TValue = unknown> = (ctx: FieldContext<TValue>, params?: Record<string, unknown>) => ValidationError$1 | ValidationError$1[] | null;
|
|
896
|
-
/**
|
|
897
|
-
* Async custom validator configuration using Angular's resource-based API
|
|
898
|
-
*
|
|
899
|
-
* @template TValue The type of value stored in the field being validated
|
|
900
|
-
* @template TParams The type of params passed to the resource
|
|
901
|
-
* @template TResult The type of result returned by the resource
|
|
902
|
-
*/
|
|
903
|
-
interface AsyncCustomValidator<TValue = unknown, TParams = unknown, TResult = unknown> {
|
|
904
|
-
/**
|
|
905
|
-
* Function that receives field context and returns resource params.
|
|
906
|
-
* Params will be tracked as a signal and trigger resource reload when changed.
|
|
907
|
-
*/
|
|
908
|
-
readonly params: (ctx: FieldContext<TValue>, config?: Record<string, unknown>) => TParams;
|
|
909
|
-
/**
|
|
910
|
-
* Function that creates a ResourceRef from the params signal.
|
|
911
|
-
* The resource will be automatically managed by Angular's lifecycle.
|
|
912
|
-
*/
|
|
913
|
-
readonly factory: (params: Signal<TParams | undefined>) => ResourceRef<TResult | undefined>;
|
|
914
|
-
/**
|
|
915
|
-
* Optional function to map successful resource result to validation errors.
|
|
916
|
-
* Return null if validation passes, or ValidationError/ValidationError[] if validation fails.
|
|
917
|
-
*/
|
|
918
|
-
readonly onSuccess?: (result: TResult, ctx: FieldContext<TValue>) => TreeValidationResult;
|
|
919
|
-
/**
|
|
920
|
-
* Optional error handler for resource errors (network failures, etc.).
|
|
921
|
-
* Return null to ignore the error, or ValidationError to display it to the user.
|
|
922
|
-
*/
|
|
923
|
-
readonly onError?: (error: unknown, ctx: FieldContext<TValue>) => ValidationError$1 | ValidationError$1[] | null;
|
|
924
|
-
}
|
|
925
|
-
/** HTTP request configuration for validateHttp API */
|
|
926
|
-
interface HttpResourceRequest {
|
|
927
|
-
url: string;
|
|
928
|
-
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
929
|
-
body?: unknown;
|
|
930
|
-
headers?: Record<string, string | string[]>;
|
|
931
|
-
}
|
|
932
|
-
/**
|
|
933
|
-
* HTTP-based validator configuration for Angular's validateHttp API
|
|
934
|
-
*
|
|
935
|
-
* @template TValue The type of value stored in the field being validated
|
|
936
|
-
* @template TResult The type of HTTP response
|
|
937
|
-
*/
|
|
938
|
-
interface HttpCustomValidator<TValue = unknown, TResult = unknown> {
|
|
939
|
-
/**
|
|
940
|
-
* Function that returns the HTTP request configuration.
|
|
941
|
-
* Can return:
|
|
942
|
-
* - `undefined` to skip validation (e.g., if field is empty)
|
|
943
|
-
* - `string` for simple GET requests (just the URL)
|
|
944
|
-
* - `HttpResourceRequest` for full control (method, body, headers)
|
|
945
|
-
*/
|
|
946
|
-
readonly request: (ctx: FieldContext<TValue>, config?: Record<string, unknown>) => string | HttpResourceRequest | undefined;
|
|
947
|
-
/** REQUIRED function to map successful HTTP response to validation errors. */
|
|
948
|
-
readonly onSuccess: (result: TResult, ctx: FieldContext<TValue>) => TreeValidationResult;
|
|
949
|
-
/**
|
|
950
|
-
* Optional error handler for HTTP errors (network failures, 4xx/5xx status codes).
|
|
951
|
-
* Return null to ignore the error, or ValidationError to display it to the user.
|
|
952
|
-
*/
|
|
953
|
-
readonly onError?: (error: unknown, ctx: FieldContext<TValue>) => ValidationError$1 | ValidationError$1[] | null;
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
/** Mapping configuration for interpreting HTTP responses as validation results. */
|
|
957
|
-
interface HttpValidationResponseMapping {
|
|
958
|
-
/** Expression evaluated with scope `{ response }`. Must evaluate to `true` (strict boolean) to be valid. */
|
|
959
|
-
validWhen: string;
|
|
960
|
-
/** Error kind returned when validation fails — maps to field-level `validationMessages` */
|
|
961
|
-
errorKind: string;
|
|
962
|
-
/**
|
|
963
|
-
* Parameters to include in the validation error for message interpolation.
|
|
964
|
-
* Map of parameter names to expressions evaluated against `{ response }`.
|
|
965
|
-
*/
|
|
966
|
-
errorParams?: Record<string, string>;
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
/** Base configuration shared by all validators */
|
|
970
|
-
interface BaseValidatorConfig {
|
|
971
|
-
/** Conditional logic for when validator applies */
|
|
972
|
-
when?: ConditionalExpression;
|
|
973
|
-
}
|
|
974
|
-
/** Built-in validator configuration (required, email, min, max, etc.) */
|
|
975
|
-
interface BuiltInValidatorConfig extends BaseValidatorConfig {
|
|
976
|
-
/** Validator type identifier */
|
|
977
|
-
type: 'required' | 'email' | 'min' | 'max' | 'minLength' | 'maxLength' | 'pattern';
|
|
978
|
-
/** Static value for the validator (e.g., min value, pattern) */
|
|
979
|
-
value?: number | string | RegExp;
|
|
980
|
-
/** Dynamic value expression that evaluates to validator parameter */
|
|
981
|
-
expression?: string;
|
|
982
|
-
}
|
|
983
|
-
/**
|
|
984
|
-
* Custom validator configuration using Angular's public FieldContext API.
|
|
985
|
-
* Returns ValidationError | ValidationError[] | null synchronously.
|
|
986
|
-
*/
|
|
987
|
-
interface CustomValidatorConfig extends BaseValidatorConfig {
|
|
988
|
-
/** Validator type identifier */
|
|
989
|
-
type: 'custom';
|
|
990
|
-
/** Name of registered validator function (function-based pattern) */
|
|
991
|
-
functionName?: string;
|
|
992
|
-
/**
|
|
993
|
-
* Inline custom validator (code-only authoring).
|
|
994
|
-
* Mutually exclusive with `functionName`. NOT JSON-serializable.
|
|
995
|
-
*/
|
|
996
|
-
fn?: CustomValidator;
|
|
997
|
-
/** Optional parameters to pass to validator function */
|
|
998
|
-
params?: Record<string, unknown>;
|
|
999
|
-
/** JavaScript expression to evaluate (expression-based pattern) */
|
|
1000
|
-
expression?: string;
|
|
1001
|
-
/** Error kind for expression-based validators - links to validationMessages */
|
|
1002
|
-
kind?: string;
|
|
1003
|
-
/**
|
|
1004
|
-
* Parameters to include in error object for message interpolation (expression-based pattern)
|
|
1005
|
-
* Map of parameter names to expressions that will be evaluated in the same context as the validation expression
|
|
1006
|
-
*/
|
|
1007
|
-
errorParams?: Record<string, string>;
|
|
1008
|
-
}
|
|
1100
|
+
} ? InferContainerChildren<F, Depth[D], unknown>[] : unknown[];
|
|
1009
1101
|
/**
|
|
1010
|
-
*
|
|
1011
|
-
*
|
|
1012
|
-
*
|
|
1102
|
+
* Recurse into a container's child fields, but only when they form a fixed-length
|
|
1103
|
+
* tuple — i.e. a config authored with `as const`. A general `RegisteredFieldTypes[]`
|
|
1104
|
+
* array (notably the base default `InferFormValue<RegisteredFieldTypes[]>` over the
|
|
1105
|
+
* whole registry) bails to `TFallback`; recursing there would make the type explode
|
|
1106
|
+
* past TypeScript's union-complexity limit (TS2590) and break consumers of the
|
|
1107
|
+
* abstract `FormConfig`. `[...F]` re-mutabilises the readonly tuple for the inner
|
|
1108
|
+
* `InferFormValueWithDepth` constraint.
|
|
1013
1109
|
*/
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
}
|
|
1110
|
+
type InferContainerChildren<F, D extends number, TFallback> = F extends readonly RegisteredFieldTypes[] ? number extends F['length'] ? TFallback : InferFormValueWithDepth<[...F], D> : TFallback;
|
|
1111
|
+
/** Process a single field and determine its contribution to the form value type */
|
|
1112
|
+
type ProcessField<T, D extends number = 5> = [D] extends [never] ? Record<string, unknown> : T extends {
|
|
1113
|
+
type: 'page' | 'row';
|
|
1114
|
+
fields: infer F;
|
|
1115
|
+
} ? InferContainerChildren<F, Depth[D], never> : T extends {
|
|
1116
|
+
type: 'group';
|
|
1117
|
+
key: infer K;
|
|
1118
|
+
fields: infer F;
|
|
1119
|
+
} ? K extends string ? {
|
|
1120
|
+
[P in K]: InferContainerChildren<F, Depth[D], Record<string, unknown>>;
|
|
1121
|
+
} : never : T extends {
|
|
1122
|
+
type: 'array';
|
|
1123
|
+
key: infer K;
|
|
1124
|
+
} ? K extends string ? {
|
|
1125
|
+
[P in K]: InferArrayValue<T, D>;
|
|
1126
|
+
} : never : T extends {
|
|
1127
|
+
type: 'text';
|
|
1128
|
+
} ? never : T extends {
|
|
1129
|
+
type: 'submit' | 'button' | 'next' | 'previous' | 'addArrayItem' | 'add-array-item' | 'prependArrayItem' | 'prepend-array-item' | 'insertArrayItem' | 'insert-array-item' | 'removeArrayItem' | 'remove-array-item' | 'popArrayItem' | 'pop-array-item' | 'shiftArrayItem' | 'shift-array-item';
|
|
1130
|
+
} ? never : T extends {
|
|
1131
|
+
key: infer K;
|
|
1132
|
+
value: infer V;
|
|
1133
|
+
} ? K extends string ? {
|
|
1134
|
+
[P in K]: MaybeOptional<T, InferValueType<T, V>>;
|
|
1135
|
+
} : never : T extends {
|
|
1136
|
+
key: infer K;
|
|
1137
|
+
} ? K extends string ? {
|
|
1138
|
+
[P in K]: MaybeOptional<T, T extends {
|
|
1139
|
+
nullable: true;
|
|
1140
|
+
} ? string | null : string>;
|
|
1141
|
+
} : never : never;
|
|
1142
|
+
/** Internal helper with depth tracking */
|
|
1143
|
+
type InferFormValueWithDepth<T extends RegisteredFieldTypes[], D extends number = 5> = UnionToIntersection<ProcessField<T[number], D>>;
|
|
1144
|
+
/** Helper to extract fields from either fields array or FormConfig */
|
|
1145
|
+
type ExtractFields<T> = T extends {
|
|
1146
|
+
fields: infer TFields;
|
|
1147
|
+
} ? TFields extends readonly RegisteredFieldTypes[] ? TFields : never : T extends readonly RegisteredFieldTypes[] ? T : never;
|
|
1020
1148
|
/**
|
|
1021
|
-
*
|
|
1022
|
-
*
|
|
1149
|
+
* Infer form value type from fields array or FormConfig.
|
|
1150
|
+
* Recursively processes nested structures and merges the results.
|
|
1151
|
+
* Limited to 5 levels of nesting to prevent infinite type instantiation.
|
|
1023
1152
|
*/
|
|
1024
|
-
type
|
|
1025
|
-
/** Name of registered async validator function */
|
|
1026
|
-
functionName: string;
|
|
1027
|
-
/** Inline form is forbidden when `functionName` is set */
|
|
1028
|
-
fn?: never;
|
|
1029
|
-
}) | (AsyncValidatorConfigShared & {
|
|
1030
|
-
/** Inline async validator. Mutually exclusive with `functionName`. */
|
|
1031
|
-
fn: AsyncCustomValidator;
|
|
1032
|
-
/** Registered form is forbidden when `fn` is set */
|
|
1033
|
-
functionName?: never;
|
|
1034
|
-
});
|
|
1153
|
+
type InferFormValue<T> = ExtractFields<T> extends readonly RegisteredFieldTypes[] ? InferFormValueWithDepth<ExtractFields<T> extends RegisteredFieldTypes[] ? ExtractFields<T> : [...ExtractFields<T>], 5> : never;
|
|
1035
1154
|
/**
|
|
1036
|
-
*
|
|
1155
|
+
* Default `TModel` for `DynamicForm` / `FormStateManager` generics.
|
|
1037
1156
|
*
|
|
1038
|
-
*
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
/** Name of registered HTTP validator configuration */
|
|
1049
|
-
functionName: string;
|
|
1050
|
-
/** Inline form is forbidden when `functionName` is set */
|
|
1051
|
-
fn?: never;
|
|
1052
|
-
}) | (FunctionHttpValidatorConfigShared & {
|
|
1053
|
-
/** Inline HTTP validator. Mutually exclusive with `functionName`. */
|
|
1054
|
-
fn: HttpCustomValidator;
|
|
1055
|
-
/** Registered form is forbidden when `fn` is set */
|
|
1056
|
-
functionName?: never;
|
|
1057
|
-
});
|
|
1058
|
-
/** Declarative HTTP validator configuration — fully JSON-serializable, no function registration required. */
|
|
1059
|
-
interface DeclarativeHttpValidatorConfig extends BaseValidatorConfig {
|
|
1060
|
-
/** Validator type identifier */
|
|
1061
|
-
type: 'http';
|
|
1062
|
-
/** HTTP request configuration with expression-based query params and body */
|
|
1063
|
-
http: HttpRequestConfig;
|
|
1064
|
-
/** Mapping that interprets the HTTP response as a validation result */
|
|
1065
|
-
responseMapping: HttpValidationResponseMapping;
|
|
1066
|
-
}
|
|
1067
|
-
/**
|
|
1068
|
-
* Configuration for signal forms validator functions that can be serialized from API.
|
|
1069
|
-
* Discriminated union type for type-safe validator configuration.
|
|
1157
|
+
* Equals `InferFormValue<T>` for any concrete config (an object type). Falls back
|
|
1158
|
+
* to `Record<string, unknown>` only when inference yields a non-record — e.g. an
|
|
1159
|
+
* empty `fields` array, where `InferFormValue` is `unknown` — so the `TModel
|
|
1160
|
+
* extends Record<string, unknown>` bound always holds by construction.
|
|
1161
|
+
*
|
|
1162
|
+
* Crucially, unlike the older `InferFormValue<T> & Record<string, unknown>`
|
|
1163
|
+
* default, this does NOT graft an index signature onto the model. That matters at
|
|
1164
|
+
* the consumer call site: the index signature made `value['anyTypo']` resolve to
|
|
1165
|
+
* `unknown` instead of erroring, silently defeating the typo-safety the inference
|
|
1166
|
+
* is meant to provide.
|
|
1070
1167
|
*/
|
|
1071
|
-
type
|
|
1072
|
-
/** Type guard to distinguish function-based HTTP validators from declarative ones. */
|
|
1073
|
-
declare function isFunctionHttpValidator(config: FunctionHttpValidatorConfig | DeclarativeHttpValidatorConfig): config is FunctionHttpValidatorConfig;
|
|
1168
|
+
type InferFormModel<T, M = InferFormValue<T>> = M extends Record<string, unknown> ? M : Record<string, unknown>;
|
|
1074
1169
|
|
|
1075
1170
|
/** Special form-state conditions for button disabled logic. */
|
|
1076
1171
|
type FormStateCondition =
|
|
@@ -1454,29 +1549,6 @@ interface SchemaDefinition {
|
|
|
1454
1549
|
subSchemas?: SchemaApplicationConfig[];
|
|
1455
1550
|
}
|
|
1456
1551
|
|
|
1457
|
-
/**
|
|
1458
|
-
* Re-export Angular's ValidationError for consistency
|
|
1459
|
-
* This replaces our custom ValidationError interface
|
|
1460
|
-
*/
|
|
1461
|
-
type ValidationError = ValidationError$1;
|
|
1462
|
-
/**
|
|
1463
|
-
* Resolves a validation message from the validation error, so error params
|
|
1464
|
-
* (e.g. maxLength, min) can be passed to an i18n layer natively.
|
|
1465
|
-
*/
|
|
1466
|
-
type ValidationMessageResolver = (error: ValidationError) => DynamicText;
|
|
1467
|
-
/** A validation message: static or reactive text, or a function of the validation error. */
|
|
1468
|
-
type ValidationMessage = DynamicText | ValidationMessageResolver;
|
|
1469
|
-
interface ValidationMessages {
|
|
1470
|
-
required?: ValidationMessage;
|
|
1471
|
-
email?: ValidationMessage;
|
|
1472
|
-
min?: ValidationMessage;
|
|
1473
|
-
max?: ValidationMessage;
|
|
1474
|
-
minLength?: ValidationMessage;
|
|
1475
|
-
maxLength?: ValidationMessage;
|
|
1476
|
-
pattern?: ValidationMessage;
|
|
1477
|
-
[key: string]: ValidationMessage | undefined;
|
|
1478
|
-
}
|
|
1479
|
-
|
|
1480
1552
|
/**
|
|
1481
1553
|
* The result type for submission actions.
|
|
1482
1554
|
* Can be either a Promise or an Observable.
|
|
@@ -1583,6 +1655,66 @@ interface FormOptions {
|
|
|
1583
1655
|
submitButton?: SubmitButtonOptions;
|
|
1584
1656
|
/** Default disabled behavior for next page buttons. */
|
|
1585
1657
|
nextButton?: NextButtonOptions;
|
|
1658
|
+
/**
|
|
1659
|
+
* For paged forms: which page to start on. Use for deep links and session restore.
|
|
1660
|
+
*
|
|
1661
|
+
* Applied by the orchestrator as it initializes, so it is race-free even when the
|
|
1662
|
+
* config arrives asynchronously. Out-of-range indices clamp to the last page;
|
|
1663
|
+
* negative or non-finite values fall back to `0`. A hidden target resolves to the
|
|
1664
|
+
* nearest visible page.
|
|
1665
|
+
*
|
|
1666
|
+
* The shorthand `initialPage: 4` lands on the page unconditionally, which is what
|
|
1667
|
+
* restore usually wants. Pass `{ index, validate: true }` to run the same validity
|
|
1668
|
+
* gate a forward `GoToPageEvent` uses, which stops on the first invalid page.
|
|
1669
|
+
*
|
|
1670
|
+
* A gated landing resolves once at mount, so supply restored values with the config.
|
|
1671
|
+
*
|
|
1672
|
+
* @default undefined (starts on page 0)
|
|
1673
|
+
*/
|
|
1674
|
+
initialPage?: number | InitialPageConfig;
|
|
1675
|
+
/**
|
|
1676
|
+
* For paged forms: how many pages on each side of the current page are mounted
|
|
1677
|
+
* during idle time (the "preload window"). The active page renders directly;
|
|
1678
|
+
* pages outside the window remain lightweight placeholders.
|
|
1679
|
+
*
|
|
1680
|
+
* `0` mounts only the current page; `1` preloads ±1 on idle for flicker-free sequential
|
|
1681
|
+
* navigation; higher values pre-warm more pages for jump navigation at the cost
|
|
1682
|
+
* of more initial DOM + change detection.
|
|
1683
|
+
*
|
|
1684
|
+
* Overrides the global `withPagePreload(n)` default for this form.
|
|
1685
|
+
*
|
|
1686
|
+
* @default undefined (uses global setting, which defaults to `1`)
|
|
1687
|
+
*/
|
|
1688
|
+
pagePreloadWindow?: number;
|
|
1689
|
+
/**
|
|
1690
|
+
* For flat (single-page) forms: progressive field mounting ("field
|
|
1691
|
+
* windowing"). Fields beyond the eager count render as a placeholder and
|
|
1692
|
+
* mount only once scrolled near/into view, instead of all mounting eagerly.
|
|
1693
|
+
*
|
|
1694
|
+
* `true` enables windowing using the global `withFieldWindowing()` defaults;
|
|
1695
|
+
* `false` force-disables it even if the global feature is enabled; an object
|
|
1696
|
+
* enables it with per-form `eager` / `placeholderHeight` / `park` overrides.
|
|
1697
|
+
*
|
|
1698
|
+
* `park` holds scrolled-away fields out of change detection while leaving
|
|
1699
|
+
* their DOM in place, so they stay findable, autofillable and reachable by
|
|
1700
|
+
* assistive tech. It is off unless `withFieldWindowing()` is used, because it
|
|
1701
|
+
* suspends model → DOM updates for a scrolled-away field until it returns.
|
|
1702
|
+
* Set `park: true` to opt in without deferred mounting, `park: false` to opt
|
|
1703
|
+
* out, or `park: { margin }` to change how far outside the viewport stays live.
|
|
1704
|
+
* The margin follows `IntersectionObserver.rootMargin` and accepts one to four
|
|
1705
|
+
* pixel or percentage values; unsupported units fall back to the inherited margin.
|
|
1706
|
+
*
|
|
1707
|
+
* Overrides the global `withFieldWindowing(...)` default for this form.
|
|
1708
|
+
*
|
|
1709
|
+
* @default undefined (uses global setting, which defaults to disabled)
|
|
1710
|
+
*/
|
|
1711
|
+
fieldWindowing?: boolean | {
|
|
1712
|
+
eager?: number;
|
|
1713
|
+
placeholderHeight?: string;
|
|
1714
|
+
park?: boolean | {
|
|
1715
|
+
margin?: string;
|
|
1716
|
+
};
|
|
1717
|
+
};
|
|
1586
1718
|
/**
|
|
1587
1719
|
* Whether to exclude values of hidden fields from submission output.
|
|
1588
1720
|
*
|
|
@@ -1613,6 +1745,67 @@ interface FormOptions {
|
|
|
1613
1745
|
* @default undefined (uses global setting)
|
|
1614
1746
|
*/
|
|
1615
1747
|
emitFormValueOnEvents?: boolean;
|
|
1748
|
+
/**
|
|
1749
|
+
* Exposes this form to browser AI agents as WebMCP tools.
|
|
1750
|
+
*
|
|
1751
|
+
* Requires the `withExperimentalWebMcp()` feature on `provideDynamicForm(...)`. Registers
|
|
1752
|
+
* `fill_{name}`, which applies a partial patch of values and reports the
|
|
1753
|
+
* form's state back. Submission stays off unless `allowSubmit` is set.
|
|
1754
|
+
*
|
|
1755
|
+
* Experimental: WebMCP is an emerging standard and Angular's underlying APIs
|
|
1756
|
+
* are marked experimental, so this may change outside a major version.
|
|
1757
|
+
*
|
|
1758
|
+
* @example
|
|
1759
|
+
* ```typescript
|
|
1760
|
+
* options: { webMcp: { name: 'invoice', description: 'Create an invoice for a customer.' } }
|
|
1761
|
+
* ```
|
|
1762
|
+
*
|
|
1763
|
+
* @default undefined (form is not exposed to agents)
|
|
1764
|
+
*/
|
|
1765
|
+
webMcp?: WebMcpToolOptions;
|
|
1766
|
+
}
|
|
1767
|
+
/** Identifies a form's WebMCP tools to connected AI agents. */
|
|
1768
|
+
interface WebMcpToolOptions {
|
|
1769
|
+
/**
|
|
1770
|
+
* Base name for this form's tools, used as `fill_{name}` and (when submission
|
|
1771
|
+
* is allowed) `submit_{name}`. Must be unique across every form on the page.
|
|
1772
|
+
*/
|
|
1773
|
+
name: string;
|
|
1774
|
+
/** What this form is for, and when an agent should reach for it. */
|
|
1775
|
+
description: string;
|
|
1776
|
+
/**
|
|
1777
|
+
* Whether an agent may submit this form directly.
|
|
1778
|
+
*
|
|
1779
|
+
* Off by default. Every registered tool is callable by any agent that reaches
|
|
1780
|
+
* the page, including one acting on injected instructions from elsewhere, so
|
|
1781
|
+
* submission is treated as the consequential step and kept opt-in. With it
|
|
1782
|
+
* off, an agent can still fill the form and a human presses the button.
|
|
1783
|
+
*
|
|
1784
|
+
* Leave it off for anything that spends money, sends a message, or cannot be
|
|
1785
|
+
* undone.
|
|
1786
|
+
*
|
|
1787
|
+
* @default false
|
|
1788
|
+
*/
|
|
1789
|
+
allowSubmit?: boolean;
|
|
1790
|
+
/**
|
|
1791
|
+
* How much of the form a tool response hands back to the agent.
|
|
1792
|
+
*
|
|
1793
|
+
* `'changed'` (the default) returns only the values the call itself set, plus
|
|
1794
|
+
* which fields apply, which are still empty, and any validation errors. That
|
|
1795
|
+
* is enough for an agent to orient itself and correct its own work without
|
|
1796
|
+
* being handed every value the form already held — prefilled personal data,
|
|
1797
|
+
* identifiers, anything a user typed before the agent arrived.
|
|
1798
|
+
*
|
|
1799
|
+
* `'all'` returns the whole model, minus fields whose `webMcp.readable` is
|
|
1800
|
+
* off. Choose it deliberately: every registered tool is callable by any agent
|
|
1801
|
+
* that reaches the page, and Chrome's guidance is that even a read-only tool
|
|
1802
|
+
* can reveal user information.
|
|
1803
|
+
*
|
|
1804
|
+
* @see https://developer.chrome.com/docs/ai/webmcp/secure-tools
|
|
1805
|
+
*
|
|
1806
|
+
* @default 'changed'
|
|
1807
|
+
*/
|
|
1808
|
+
readback?: 'changed' | 'all';
|
|
1616
1809
|
}
|
|
1617
1810
|
/** Options for controlling submit button disabled behavior. */
|
|
1618
1811
|
interface SubmitButtonOptions {
|
|
@@ -1629,6 +1822,18 @@ interface SubmitButtonOptions {
|
|
|
1629
1822
|
*/
|
|
1630
1823
|
disableWhileSubmitting?: boolean;
|
|
1631
1824
|
}
|
|
1825
|
+
/** Which page a paged form starts on, for deep links and session restore. */
|
|
1826
|
+
interface InitialPageConfig {
|
|
1827
|
+
/** Target page index (0-based). */
|
|
1828
|
+
index: number;
|
|
1829
|
+
/**
|
|
1830
|
+
* Whether to apply the forward-jump validity gate, stopping on the first
|
|
1831
|
+
* invalid page instead of landing on the target.
|
|
1832
|
+
*
|
|
1833
|
+
* @default false
|
|
1834
|
+
*/
|
|
1835
|
+
validate?: boolean;
|
|
1836
|
+
}
|
|
1632
1837
|
/** Options for controlling next page button disabled behavior. */
|
|
1633
1838
|
interface NextButtonOptions {
|
|
1634
1839
|
/**
|
|
@@ -1790,6 +1995,24 @@ interface RowWrapper {
|
|
|
1790
1995
|
readonly type: 'row';
|
|
1791
1996
|
}
|
|
1792
1997
|
|
|
1998
|
+
/**
|
|
1999
|
+
* Renders the wrapped field's validation errors, for a leaf or a container.
|
|
2000
|
+
*
|
|
2001
|
+
* On a container it is the only thing that can render the message — a `group` / `array`
|
|
2002
|
+
* has no adapter field component — so it is appended automatically when the container
|
|
2003
|
+
* declares `validators`. On a leaf it is opt-in: adding it takes error rendering over
|
|
2004
|
+
* from the field component, which suppresses its own via `FIELD_ERROR_DISPLAY`.
|
|
2005
|
+
*
|
|
2006
|
+
* To restyle it, register a `WrapperTypeDefinition` with the same
|
|
2007
|
+
* `wrapperName: 'field-errors'` — the later registration wins, so an adapter or an app
|
|
2008
|
+
* can swap in its own component without touching config.
|
|
2009
|
+
*/
|
|
2010
|
+
interface FieldErrorsWrapper {
|
|
2011
|
+
readonly type: 'field-errors';
|
|
2012
|
+
/** Messages keyed by error `kind`. Containers forward their own `validationMessages`. */
|
|
2013
|
+
readonly validationMessages?: ValidationMessages;
|
|
2014
|
+
}
|
|
2015
|
+
|
|
1793
2016
|
/** Container fields registry - augment this interface to add custom container fields */
|
|
1794
2017
|
interface FieldRegistryContainers {
|
|
1795
2018
|
page: PageField;
|
|
@@ -1807,6 +2030,7 @@ interface FieldRegistryLeaves {
|
|
|
1807
2030
|
interface FieldRegistryWrappers {
|
|
1808
2031
|
css: CssWrapper;
|
|
1809
2032
|
row: RowWrapper;
|
|
2033
|
+
'field-errors': FieldErrorsWrapper;
|
|
1810
2034
|
}
|
|
1811
2035
|
/**
|
|
1812
2036
|
* Global interface for dynamic form field definitions with categorization
|
|
@@ -1885,6 +2109,12 @@ interface WrapperTypeDefinition<T extends WrapperConfig = WrapperConfig> {
|
|
|
1885
2109
|
loadComponent: LazyComponentLoader;
|
|
1886
2110
|
/** Field types this wrapper should auto-apply to. */
|
|
1887
2111
|
types?: readonly string[];
|
|
2112
|
+
/**
|
|
2113
|
+
* Declares that this wrapper renders the wrapped field's errors, so the default
|
|
2114
|
+
* `field-errors` wrapper is not appended next to it. Needed here as well as via
|
|
2115
|
+
* `provideFieldErrorDisplay`: the chain resolves before any component exists to ask.
|
|
2116
|
+
*/
|
|
2117
|
+
rendersFieldErrors?: boolean;
|
|
1888
2118
|
}
|
|
1889
2119
|
/** Type guard for WrapperTypeDefinition. */
|
|
1890
2120
|
declare function isWrapperTypeDefinition(value: unknown): value is WrapperTypeDefinition;
|
|
@@ -1910,9 +2140,11 @@ interface FieldWrapper {
|
|
|
1910
2140
|
readonly fieldComponent: Signal<ViewContainerRef>;
|
|
1911
2141
|
}
|
|
1912
2142
|
/** Injection token for the wrapper type registry. */
|
|
1913
|
-
declare const WRAPPER_REGISTRY: InjectionToken<Map<string, WrapperTypeDefinition<_ng_forge_dynamic_forms_internal.RowWrapper | _ng_forge_dynamic_forms_internal.CssWrapper>>>;
|
|
2143
|
+
declare const WRAPPER_REGISTRY: InjectionToken<Map<string, WrapperTypeDefinition<_ng_forge_dynamic_forms_internal.RowWrapper | _ng_forge_dynamic_forms_internal.CssWrapper | _ng_forge_dynamic_forms_internal.FieldErrorsWrapper>>>;
|
|
1914
2144
|
/** Component cache for loaded wrapper components. */
|
|
1915
2145
|
declare const WRAPPER_COMPONENT_CACHE: InjectionToken<Map<string, Type<unknown>>>;
|
|
2146
|
+
/** Pending wrapper loads, shared by preloading and the normal render path. */
|
|
2147
|
+
declare const WRAPPER_COMPONENT_LOAD_CACHE: InjectionToken<Map<string, Promise<Type<unknown> | undefined>>>;
|
|
1916
2148
|
/**
|
|
1917
2149
|
* Pre-computed reverse index: `fieldType → WrapperConfig[]` for every
|
|
1918
2150
|
* registered `WrapperTypeDefinition.types` entry. Built once in the
|
|
@@ -2352,16 +2584,10 @@ declare function isResourceBasedValidator(config: ValidatorConfig): boolean;
|
|
|
2352
2584
|
* @param context Optional context providing function scope lookup
|
|
2353
2585
|
* @returns true if the when condition references other fields
|
|
2354
2586
|
* @deprecated No longer used for validator routing; `when` conditions are applied natively
|
|
2355
|
-
* via Angular's validator `when` config
|
|
2587
|
+
* via Angular's validator `when` config, and validator expressions are applied per-field
|
|
2588
|
+
* with a reactive evaluation context — no validator config ever requires tree validation.
|
|
2356
2589
|
*/
|
|
2357
2590
|
declare function hasCrossFieldWhenCondition(config: ValidatorConfig, context?: CrossFieldDetectionContext): boolean;
|
|
2358
|
-
/**
|
|
2359
|
-
* True only when the validator must be evaluated by the root validateTree:
|
|
2360
|
-
* a cross-field dynamic-value/validation EXPRESSION. A `when` condition alone
|
|
2361
|
-
* never requires tree validation; it is applied natively via Angular's
|
|
2362
|
-
* validator `when` config (built-ins) or whenLogic gating (custom/async/http).
|
|
2363
|
-
*/
|
|
2364
|
-
declare function requiresTreeValidation(config: ValidatorConfig): boolean;
|
|
2365
2591
|
/**
|
|
2366
2592
|
* Detects if a StateLogicConfig has a cross-field condition.
|
|
2367
2593
|
*
|
|
@@ -2665,7 +2891,13 @@ declare class FieldContextRegistryService {
|
|
|
2665
2891
|
/** Builds an evaluation context scoped to a specific array item. */
|
|
2666
2892
|
private buildArrayScopedContext;
|
|
2667
2893
|
/**
|
|
2668
|
-
*
|
|
2894
|
+
* Builds a REACTIVE evaluation context scoped to a specific array item, fine-grained:
|
|
2895
|
+
* `formValue`/`rootFormValue` are proxies over the FieldTree so a condition subscribes
|
|
2896
|
+
* only to the fields it actually reads, instead of the whole-form value.
|
|
2897
|
+
*/
|
|
2898
|
+
private buildReactiveArrayScopedContext;
|
|
2899
|
+
/**
|
|
2900
|
+
* Resolves external data signals as a lazy, read-only view.
|
|
2669
2901
|
*
|
|
2670
2902
|
* Always reads reactively. Unlike the field value and form value (which are
|
|
2671
2903
|
* read untracked to break the validator -> state -> valid -> validator cycle),
|
|
@@ -2674,7 +2906,12 @@ declare class FieldContextRegistryService {
|
|
|
2674
2906
|
* reactively is what lets dynamic values and validators bound to externalData
|
|
2675
2907
|
* update when it changes.
|
|
2676
2908
|
*
|
|
2677
|
-
*
|
|
2909
|
+
* Fine-grained: reading `externalData.foo` subscribes to only that key's
|
|
2910
|
+
* signal (plus the record signal), so an expression referencing one key does
|
|
2911
|
+
* not re-run when other external signals change. Enumeration (spread,
|
|
2912
|
+
* `Object.keys`) resolves every key.
|
|
2913
|
+
*
|
|
2914
|
+
* @returns Lazy record of resolved external data values, or undefined if no external data.
|
|
2678
2915
|
*/
|
|
2679
2916
|
private resolveExternalData;
|
|
2680
2917
|
/** Creates a REACTIVE evaluation context for logic functions. */
|
|
@@ -2697,6 +2934,22 @@ declare class FieldContextRegistryService {
|
|
|
2697
2934
|
static ɵprov: i0.ɵɵInjectableDeclaration<FieldContextRegistryService>;
|
|
2698
2935
|
}
|
|
2699
2936
|
|
|
2937
|
+
/**
|
|
2938
|
+
* A lazy, read-only view over the form value that subscribes fine-grainedly:
|
|
2939
|
+
* reading `formValue.foo` resolves `foo` through the Signal Forms `FieldTree` and
|
|
2940
|
+
* subscribes to only that field's `value()` signal, so a condition/validator that
|
|
2941
|
+
* references one field re-runs only when that field changes (instead of every
|
|
2942
|
+
* field, as reading the whole-form value would cause).
|
|
2943
|
+
*
|
|
2944
|
+
* Whole-object operations (spread, `Object.keys`) fall back to the eager whole-form
|
|
2945
|
+
* value and subscribe to everything — correct for code that needs all keys.
|
|
2946
|
+
*
|
|
2947
|
+
* @param getRootForm - the current root `FieldTree` (structural; changes on rebuild).
|
|
2948
|
+
* @param getRootFormValue - the eager whole-form value; fallback for keys absent
|
|
2949
|
+
* from the tree and for whole-object enumeration.
|
|
2950
|
+
*/
|
|
2951
|
+
declare function createFieldValueProxy(getRootForm: () => FieldTree<unknown> | undefined, getRootFormValue: () => Record<string, unknown>): Record<string, unknown>;
|
|
2952
|
+
|
|
2700
2953
|
/** Registry service for custom functions and validators */
|
|
2701
2954
|
declare class FunctionRegistryService {
|
|
2702
2955
|
private readonly customFunctions;
|
|
@@ -2867,8 +3120,122 @@ declare class RootFormRegistryService {
|
|
|
2867
3120
|
constructor(formValue: Signal<Record<string, unknown>>, rootForm: Signal<FieldTree<Record<string, unknown>> | undefined>);
|
|
2868
3121
|
}
|
|
2869
3122
|
|
|
2870
|
-
|
|
2871
|
-
|
|
3123
|
+
/** Resolved validation error with interpolated message */
|
|
3124
|
+
interface ResolvedError {
|
|
3125
|
+
kind: string;
|
|
3126
|
+
message: string;
|
|
3127
|
+
}
|
|
3128
|
+
/**
|
|
3129
|
+
* Factory function that creates a signal of resolved validation errors
|
|
3130
|
+
* Handles async resolution of DynamicText validation messages
|
|
3131
|
+
*
|
|
3132
|
+
* @param field - Signal containing the FieldTree, or `undefined` before it
|
|
3133
|
+
* exists (containers resolve their node lazily from the parent tree)
|
|
3134
|
+
* @param validationMessages - Signal containing custom field-level validation messages
|
|
3135
|
+
* @param defaultValidationMessages - Signal containing default validation messages (fallback)
|
|
3136
|
+
* @param injector - Optional injector for DynamicText resolution
|
|
3137
|
+
* @returns Signal<ResolvedError[]> - Reactively resolved error messages
|
|
3138
|
+
*/
|
|
3139
|
+
declare function createResolvedErrorsSignal<T>(field: Signal<FieldTree<T> | undefined>, validationMessages: Signal<ValidationMessages | undefined>, defaultValidationMessages?: Signal<ValidationMessages | undefined>, injector?: Injector): Signal<ResolvedError[]>;
|
|
3140
|
+
|
|
3141
|
+
/** A wrapper's claim to render errors, scoped to the one field key it wraps. */
|
|
3142
|
+
interface FieldErrorDisplayClaim {
|
|
3143
|
+
/** Key of the field whose errors this wrapper renders; `undefined` before inputs bind. */
|
|
3144
|
+
readonly claimedKey: Signal<string | undefined>;
|
|
3145
|
+
}
|
|
3146
|
+
/**
|
|
3147
|
+
* Marks that a wrapper is rendering a field's errors, so that field's component stops
|
|
3148
|
+
* rendering its own and the message appears once.
|
|
3149
|
+
*
|
|
3150
|
+
* The claim carries a key because a wrapper's providers reach every descendant: a wrapper
|
|
3151
|
+
* around an `array` would otherwise silence the errors of every input inside it. A field
|
|
3152
|
+
* suppresses its own rendering only when the claimed key is its own.
|
|
3153
|
+
*
|
|
3154
|
+
* Not `multi` — element injectors shadow rather than merge, so a nested claim would hide
|
|
3155
|
+
* the outer one regardless.
|
|
3156
|
+
*/
|
|
3157
|
+
declare const FIELD_ERROR_DISPLAY: InjectionToken<FieldErrorDisplayClaim>;
|
|
3158
|
+
/** The error surface a wrapper gets, mirroring what `NgForgeField` exposes to field components. */
|
|
3159
|
+
interface FieldErrors {
|
|
3160
|
+
/** Every resolved error, regardless of whether it should be shown yet. */
|
|
3161
|
+
readonly errors: Signal<ResolvedError[]>;
|
|
3162
|
+
/** Whether errors are ready to show — invalid, touched, and non-empty. */
|
|
3163
|
+
readonly showErrors: Signal<boolean>;
|
|
3164
|
+
/** `errors()` gated by `showErrors()`. This is what a template should render. */
|
|
3165
|
+
readonly errorsToDisplay: Signal<ResolvedError[]>;
|
|
3166
|
+
/** `{key}-error`, for wiring `aria-describedby` on the wrapped control. */
|
|
3167
|
+
readonly errorId: Signal<string>;
|
|
3168
|
+
}
|
|
3169
|
+
interface FieldErrorsOptions {
|
|
3170
|
+
/** The wrapper's `fieldInputs` input. */
|
|
3171
|
+
readonly fieldInputs: Signal<WrapperFieldInputs | undefined>;
|
|
3172
|
+
/** Overrides the messages in `fieldInputs` — containers pass their own. */
|
|
3173
|
+
readonly validationMessages?: Signal<ValidationMessages | undefined>;
|
|
3174
|
+
/** Defaults to the current injection context. */
|
|
3175
|
+
readonly injector?: Injector;
|
|
3176
|
+
}
|
|
3177
|
+
/**
|
|
3178
|
+
* Resolves the wrapped field's errors for a wrapper component, for a leaf or a container.
|
|
3179
|
+
*
|
|
3180
|
+
* A wrapper cannot `inject(NgForgeField)` — the chain is built before the field component
|
|
3181
|
+
* exists — so this reads the same information out of the `fieldInputs` bag it already has.
|
|
3182
|
+
* A leaf carries its own `field`; a container has none, so its node is resolved by key
|
|
3183
|
+
* against the parent tree in `FIELD_SIGNAL_CONTEXT`.
|
|
3184
|
+
*
|
|
3185
|
+
* Must be called in an injection context.
|
|
3186
|
+
*/
|
|
3187
|
+
declare function injectFieldErrors(options: FieldErrorsOptions): FieldErrors;
|
|
3188
|
+
|
|
3189
|
+
/** The error to raise, minus the `fieldTree` that {@link rowError} fills in. */
|
|
3190
|
+
interface RowErrorSpec {
|
|
3191
|
+
/** Error kind, matched against `validationMessages`. */
|
|
3192
|
+
readonly kind: string;
|
|
3193
|
+
/**
|
|
3194
|
+
* Message shown for this error. A row-targeted error needs its own, because the
|
|
3195
|
+
* container's `validationMessages` are keyed to the container, not to the child the
|
|
3196
|
+
* error moves to.
|
|
3197
|
+
*/
|
|
3198
|
+
readonly message?: string;
|
|
3199
|
+
readonly [param: string]: unknown;
|
|
3200
|
+
}
|
|
3201
|
+
/**
|
|
3202
|
+
* Targets a validation error at one field of one row, from a validator declared on the
|
|
3203
|
+
* array (issue #568).
|
|
3204
|
+
*
|
|
3205
|
+
* A container validator sees the whole item list, which is what makes per-row rules
|
|
3206
|
+
* expressible at all — but one message for the list cannot say which row is wrong.
|
|
3207
|
+
* Returning the result of this helper puts the error on that row's own input, where the
|
|
3208
|
+
* adapter renders it like any other field error.
|
|
3209
|
+
*
|
|
3210
|
+
* ```typescript
|
|
3211
|
+
* const periodOrder: CustomValidator = (ctx) => {
|
|
3212
|
+
* const rows = (ctx.value() ?? []) as { from?: string; to?: string }[];
|
|
3213
|
+
* return rows.flatMap((row, i) =>
|
|
3214
|
+
* row.from && row.to && row.to < row.from
|
|
3215
|
+
* ? [rowError(ctx, i, 'to', { kind: 'periodOrder', message: 'The end must not be before the start.' })]
|
|
3216
|
+
* : [],
|
|
3217
|
+
* );
|
|
3218
|
+
* };
|
|
3219
|
+
* ```
|
|
3220
|
+
*
|
|
3221
|
+
* Returns the error untargeted when the row or field cannot be resolved, so it still
|
|
3222
|
+
* surfaces on the container rather than vanishing.
|
|
3223
|
+
*/
|
|
3224
|
+
declare function rowError(ctx: FieldContext<unknown>, index: number, key: string, error: RowErrorSpec): ValidationError$1;
|
|
3225
|
+
|
|
3226
|
+
/**
|
|
3227
|
+
* Computed signal that determines if errors should be displayed.
|
|
3228
|
+
* Based on field's invalid, touched, and error count.
|
|
3229
|
+
*
|
|
3230
|
+
* @param field - Signal containing the FieldTree, or `undefined` before it exists
|
|
3231
|
+
* @returns Signal<boolean> - True if errors should be displayed
|
|
3232
|
+
*/
|
|
3233
|
+
declare function shouldShowErrors<T>(field: Signal<FieldTree<T> | undefined>): Signal<boolean>;
|
|
3234
|
+
|
|
3235
|
+
/** `field` = `validate()`; `tree` = `validateTree()`, which re-homes errors carrying a `fieldTree`. */
|
|
3236
|
+
type ValidatorAttachment = 'field' | 'tree';
|
|
3237
|
+
declare function applyValidator(config: ValidatorConfig, fieldPath: SchemaPath<any> | SchemaPathTree<any>, attachment?: ValidatorAttachment): void;
|
|
3238
|
+
declare function applyValidators(configs: ValidatorConfig[], fieldPath: SchemaPath<any> | SchemaPathTree<any>, attachment?: ValidatorAttachment): void;
|
|
2872
3239
|
|
|
2873
3240
|
/**
|
|
2874
3241
|
* Create a dynamic value function from an expression string.
|
|
@@ -2917,7 +3284,7 @@ interface BaseCheckedField<TProps, TMeta extends FieldMeta = FieldMeta, TNullabl
|
|
|
2917
3284
|
nullable?: TNullable;
|
|
2918
3285
|
}
|
|
2919
3286
|
declare function isCheckedField<TProps, TMeta extends FieldMeta = FieldMeta>(field: FieldDef<TProps, TMeta>): field is BaseCheckedField<TProps, TMeta, boolean>;
|
|
2920
|
-
type ExcludedKeys$1 = 'type' | 'conditionals' | 'value' | 'nullable' | 'disabled' | 'readonly' | 'hidden' | 'col' | keyof FieldWithValidation | 'excludeValueIfHidden' | 'excludeValueIfDisabled' | 'excludeValueIfReadonly' | 'validateWhenHidden' | 'wrappers' | 'skipAutoWrappers' | 'skipDefaultWrappers' | 'addons';
|
|
3287
|
+
type ExcludedKeys$1 = 'type' | 'conditionals' | 'value' | 'nullable' | 'disabled' | 'readonly' | 'hidden' | 'col' | keyof FieldWithValidation | 'excludeValueIfHidden' | 'excludeValueIfDisabled' | 'excludeValueIfReadonly' | 'validateWhenHidden' | 'wrappers' | 'skipAutoWrappers' | 'skipDefaultWrappers' | 'addons' | 'webMcp';
|
|
2921
3288
|
type CheckedFieldComponent<T extends BaseCheckedField<Record<string, unknown> | unknown, FieldMeta, boolean>> = Prettify<WithInputSignals<Omit<T, ExcludedKeys$1>>>;
|
|
2922
3289
|
|
|
2923
3290
|
/**
|
|
@@ -2941,7 +3308,7 @@ interface BaseValueField<TProps, TValue, TMeta extends FieldMeta = FieldMeta, TN
|
|
|
2941
3308
|
nullable?: TNullable;
|
|
2942
3309
|
}
|
|
2943
3310
|
declare function isValueField<TProps, TMeta extends FieldMeta = FieldMeta>(field: FieldDef<TProps, TMeta>): field is BaseValueField<TProps, ValueType, TMeta, boolean>;
|
|
2944
|
-
type ExcludedKeys = 'type' | 'conditionals' | 'value' | 'valueType' | 'nullable' | 'disabled' | 'readonly' | 'hidden' | 'col' | 'minValue' | 'maxValue' | 'step' | 'required' | 'email' | 'min' | 'max' | 'minLength' | 'maxLength' | 'pattern' | 'validators' | 'logic' | 'derivation' | 'schemas' | 'excludeValueIfHidden' | 'excludeValueIfDisabled' | 'excludeValueIfReadonly' | 'validateWhenHidden' | 'wrappers' | 'skipAutoWrappers' | 'skipDefaultWrappers' | 'addons';
|
|
3311
|
+
type ExcludedKeys = 'type' | 'conditionals' | 'value' | 'valueType' | 'nullable' | 'disabled' | 'readonly' | 'hidden' | 'col' | 'minValue' | 'maxValue' | 'step' | 'required' | 'email' | 'min' | 'max' | 'minLength' | 'maxLength' | 'pattern' | 'validators' | 'logic' | 'derivation' | 'schemas' | 'excludeValueIfHidden' | 'excludeValueIfDisabled' | 'excludeValueIfReadonly' | 'validateWhenHidden' | 'wrappers' | 'skipAutoWrappers' | 'skipDefaultWrappers' | 'addons' | 'webMcp';
|
|
2945
3312
|
type ValueFieldComponent<T extends BaseValueField<Record<string, unknown> | unknown, unknown, FieldMeta, boolean>> = Prettify<WithInputSignals<Omit<T, ExcludedKeys>>>;
|
|
2946
3313
|
|
|
2947
3314
|
/** Base interface for all form events in the dynamic form system. */
|
|
@@ -3585,6 +3952,11 @@ declare function memoize<TFunc extends (...args: any[]) => any>(fn: TFunc, resol
|
|
|
3585
3952
|
* @returns Fields as array
|
|
3586
3953
|
*/
|
|
3587
3954
|
declare function normalizeFieldsArray<T>(fields: readonly T[] | Record<string, T>): T[];
|
|
3955
|
+
/**
|
|
3956
|
+
* {@link getChangedKeys} restricted to `keys`. Callers that only care about a handful of
|
|
3957
|
+
* paths avoid diffing an entire form value on every change.
|
|
3958
|
+
*/
|
|
3959
|
+
declare function getChangedKeysWithin(previous: Record<string, unknown> | null | undefined, current: Record<string, unknown> | null | undefined, keys: ReadonlySet<string>): Set<string>;
|
|
3588
3960
|
/**
|
|
3589
3961
|
* Gets the keys that differ between two objects.
|
|
3590
3962
|
*
|
|
@@ -3604,5 +3976,46 @@ declare function assertNever(value: never): never;
|
|
|
3604
3976
|
*/
|
|
3605
3977
|
declare function withPreviousValue<T>(input: Resource<T>): Resource<T>;
|
|
3606
3978
|
|
|
3607
|
-
|
|
3608
|
-
|
|
3979
|
+
/**
|
|
3980
|
+
* Plumbing shared by every `field-errors` wrapper: the slot, the inputs, the resolved
|
|
3981
|
+
* error surface, and the key this wrapper claims. Subclasses supply only a template —
|
|
3982
|
+
* which is the only thing that actually differs between the core default and each
|
|
3983
|
+
* adapter's native markup.
|
|
3984
|
+
*
|
|
3985
|
+
* ```typescript
|
|
3986
|
+
* @Component({
|
|
3987
|
+
* selector: 'df-mat-field-errors',
|
|
3988
|
+
* imports: [MatError],
|
|
3989
|
+
* template: `
|
|
3990
|
+
* <ng-container #fieldComponent></ng-container>
|
|
3991
|
+
* @if (ngf.errorsToDisplay()[0]; as error) {
|
|
3992
|
+
* <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
|
|
3993
|
+
* }
|
|
3994
|
+
* `,
|
|
3995
|
+
* providers: [provideFieldErrorDisplay(() => MatFieldErrorsWrapperComponent)],
|
|
3996
|
+
* })
|
|
3997
|
+
* export default class MatFieldErrorsWrapperComponent extends FieldErrorsWrapperBase {}
|
|
3998
|
+
* ```
|
|
3999
|
+
*/
|
|
4000
|
+
declare abstract class FieldErrorsWrapperBase implements FieldWrapper, FieldErrorDisplayClaim {
|
|
4001
|
+
readonly fieldComponent: i0.Signal<ViewContainerRef>;
|
|
4002
|
+
/** Overrides the messages in `fieldInputs`. Containers forward their own. */
|
|
4003
|
+
readonly validationMessages: i0.InputSignal<ValidationMessages | undefined>;
|
|
4004
|
+
readonly fieldInputs: i0.InputSignal<WrapperFieldInputs | undefined>;
|
|
4005
|
+
/** The field whose errors this wrapper renders — see `FieldErrorDisplayClaim`. */
|
|
4006
|
+
readonly claimedKey: i0.Signal<string | undefined>;
|
|
4007
|
+
/** Resolved error surface, mirroring what `injectNgForgeField()` gives a field component. */
|
|
4008
|
+
protected readonly ngf: _ng_forge_dynamic_forms_internal.FieldErrors;
|
|
4009
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FieldErrorsWrapperBase, never>;
|
|
4010
|
+
static ɵdir: i0.ɵɵDirectiveDeclaration<FieldErrorsWrapperBase, never, never, { "validationMessages": { "alias": "validationMessages"; "required": false; "isSignal": true; }; "fieldInputs": { "alias": "fieldInputs"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
4011
|
+
}
|
|
4012
|
+
/**
|
|
4013
|
+
* Declares that a wrapper renders the wrapped field's errors, so that field's component
|
|
4014
|
+
* renders none of its own.
|
|
4015
|
+
*
|
|
4016
|
+
* Takes a thunk because the class is still being defined when the decorator evaluates.
|
|
4017
|
+
*/
|
|
4018
|
+
declare function provideFieldErrorDisplay(wrapper: () => Type<FieldErrorDisplayClaim>): Provider;
|
|
4019
|
+
|
|
4020
|
+
export { ADDON_ACTION_HANDLERS, ADDON_ACTION_REGISTRY, ADDON_TYPE_COMPONENT_CACHE, ADDON_TYPE_DEFINITIONS, ADDON_TYPE_REGISTRY, ARRAY_CONTEXT, AsyncConditionFunctionCacheService, ComponentInitializedEvent, DEFAULT_PROPS, DEFAULT_VALIDATION_MESSAGES, DEFAULT_WRAPPERS, DEPRECATION_WARNING_TRACKER, DF_FIELD_TEMPLATES, DfAddonSlot, DynamicFormError, DynamicFormLogger, DynamicTextPipe, DynamicValueFunctionCacheService, EMIT_FORM_VALUE_ON_EVENTS, EXTERNAL_DATA, EventBus, ExpressionParser, ExpressionParserError, FIELD_ERROR_DISPLAY, FIELD_REGISTRY, FIELD_SIGNAL_CONTEXT, FORM_ID_PREFIX, FORM_OPTIONS, FieldContextRegistryService, FieldErrorsWrapperBase, FunctionRegistryService, GROUP_CONTEXT, HTTP_CONDITION_CACHE, HttpConditionCache, HttpConditionFunctionCacheService, INITIALIZATION_TIMEOUT_MS, LogicFunctionCacheService, NoopLogger, READONLY_FIELD_TREE_CACHE, RootFormRegistryService, TokenType, WRAPPER_AUTO_ASSOCIATIONS, WRAPPER_COMPONENT_CACHE, WRAPPER_COMPONENT_LOAD_CACHE, WRAPPER_REGISTRY, applyMetaToElement, applyValidator, applyValidators, arrayFieldMapper, assertNever, baseFieldMapper, buildBaseInputs, buildClassName, compareValues, containerFieldMapper, createAddonActionRegistry, createDebouncedLogicFunction, createDefaultDerivationLogConfig, createDynamicValueFunction, createFieldValueProxy, createFormFieldStateMap, createFormStateLogicFunction, createInitializationTracker, createLogicFunction, createResolvedErrorsSignal, createWarningTracker, deepMergeDefaults, derivedFromDeferred, detectFormMode, dynamicTextToObservable, evaluateCondition, evaluateNonFieldDisabled, evaluateNonFieldHidden, extractExpressionDependencies, extractStringDependencies, getArrayLength, getChangedKeys, getChangedKeysWithin, getFieldValueHandling, getGridClassString, getNestedValue, groupFieldMapper, hasChildFields, hasCrossFieldWhenCondition, hasFormValue, hasNestedProperty, hasTargetProperty, injectAddonTypeRegistry, injectFieldErrors, injectFieldSignalContext, injectFieldsSupportingAddons, injectNonFieldEvaluationContext, interpolateParams, isArrayField, isCheckedField, isContainerField, isCrossFieldBuiltInValidator, isCrossFieldExpression, isCrossFieldSchema, isCrossFieldStateLogic, isCrossFieldValidator, isDerivationLogicConfig, isDisplayOnlyField, isEqual, isFieldBoundContext, isFormStateCondition, isFunctionHttpValidator, isGenericContainerField, isGroupField, isHiddenField, isLeafField, isNonPagedForm, isPageField, isPagedForm, isResourceBasedValidator, isRowField, isSimplifiedArrayField, isStateLogicConfig, isValidNonPagedForm, isValidPagedForm, isValueBearingField, isValueField, isWrapperTypeDefinition, keyBy, mapValues, memoize, normalizeFieldsArray, omit, pageFieldMapper, provideFieldErrorDisplay, readFieldStateInfo, resolveDynamicValue, resolveHttpRequest, resolveNextButtonDisabled, resolveNonFieldDisabled, resolveNonFieldHidden, resolveSubmitButtonDisabled, resolveTokens, rowError, rowFieldMapper, runPresetAction, setupInitializationTracking, shouldLog, shouldShowErrors, textFieldMapper, toReadonlyFieldTree, toReadonlyFieldTreeCached, validatePageNesting, withPreviousValue, writeToFieldValue };
|
|
4021
|
+
export type { ASTNode, AddonActionContext, AddonActionHandler, AddonActionPreset, AddonShapeValidator, AddonSlot, AddonTypeDefinition, AddonTypeRegistryRef, AddonTypeSchema, AndCondition, AnyAddon, ArrayAllowedChildren, ArrayButtonConfig, ArrayComponent, ArrayContext, ArrayField, ArrayFieldTree, ArrayItemDefinition, ArrayItemTemplate, ArrayLiteralNode, ArrowFunctionNode, AsyncCondition, AsyncConditionFunction, AsyncCustomValidator, AsyncDerivationFunction, AsyncValidatorConfig, AvailableFieldTypes, BaseAddon, BaseCheckedField, BaseValidatorConfig, BaseValueField, BinaryOpNode, BuiltInValidatorConfig, ButtonLogicContext, CallExpressionNode, CheckedFieldComponent, CommonAddonActionPreset, CommonAddonSlot, ComparisonOperator, ComponentAddon, ComputedMemberAccessNode, ConditionalExpression, ConditionalNode, ContainerAllowedChildren, ContainerComponent, ContainerField, ContainerFieldTypes, ContainerLogicConfig, ContainerValidation, CrossFieldDetectionContext, CssWrapper, CustomCondition, CustomFnConfig, CustomFunction, CustomFunctionOptions, CustomFunctionScope, CustomValidator, CustomValidatorConfig, DeclarativeHttpValidatorConfig, DerivationLogConfig, DerivationLogLevel, DerivationLogicConfig, DerivedFromDeferredOptions, DynamicFormActionRegistry, DynamicFormAddonActionPresetRegistry, DynamicFormAddonRegistry, DynamicFormAddonSlotRegistry, DynamicFormFieldRegistry, DynamicText, DynamicValue, EvaluationContext, ExtractField, FieldAddonSupport, FieldAddonSupportEntry, FieldBoundAddonActionContext, FieldComponent, FieldDef, FieldErrorDisplayClaim, FieldErrors, FieldErrorsOptions, FieldErrorsWrapper, FieldMeta, FieldOption, FieldPathAccess, FieldRegistryContainers, FieldRegistryLeaves, FieldRegistryWrappers, FieldScope, FieldSignalContext, FieldStateContext, FieldStateInfo, FieldTreeRecord, FieldTypeDefinition, FieldValueCondition, FieldWebMcpConfig, FieldWebMcpOptions, FieldWithValidation, FieldWrapper, FormConfig, FormEvent, FormEventConstructor, FormFieldStateMap, FormMode, FormModeDetectionResult, FormOptions, FormStateCondition, FunctionHttpValidatorConfig, GroupAllowedChildren, GroupComponent, GroupContext, GroupField, HiddenField, HiddenScalar, HiddenValue, HttpCondition, HttpCustomValidator, HttpRequestConfig, HttpResourceRequest, HttpValidationResponseMapping, IdentifierNode, InferFormModel, InferFormValue, InitialPageConfig, InitializationTrackingOptions, JavascriptCondition, LazyComponentLoader, LeafFieldTypes, LiteralNode, Logger, LogicConfig, LogicTrigger, MapperFn, MemberAccessNode, MemoizeOptions, NarrowField, NarrowFields, NextButtonOptions, NonFieldLogicConfig, NonFieldLogicContext, NonFieldLogicType, ObjectLiteralNode, OrCondition, OrphanAddonActionContext, PageAllowedChildren, PageField, PresetCollaborators, Prettify, ReadonlyFieldTree, RegisteredActionRef, RegisteredFieldTypes, RegisteredWrapperTypes, RenderReadyInput, ResolvedError, ResolvedValidationExecutionConfig, ResolvedValueExclusionConfig, RowAllowedChildren, RowComponent, RowErrorSpec, RowField, RowWrapper, SchemaApplicationConfig, SchemaDefinition, SimplifiedArrayField, StateLogicConfig, StateLogicType, SubmissionActionResult, SubmissionConfig, SubmitButtonOptions, TemplateAddon, TextAddon, TextElementType, TextField, TextProps, Token, TokenContext, UnaryOpNode, ValidationError, ValidationExecutionConfig, ValidationMessage, ValidationMessageResolver, ValidationMessages, ValidatorAttachment, ValidatorConfig, ValueExclusionConfig, ValueFieldComponent, ValueHandlingMode, ValueType, WarningTracker, WebMcpToolOptions, WithInputSignals, WrapperAutoAssociations, WrapperConfig, WrapperFieldInputs, WrapperTypeDefinition };
|