@ng-forge/dynamic-forms 1.2.0-next.1 → 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-BVnXnaNw.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-CkqvXXs_.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-CM1pY9v5.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-D4JhDffB.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 +43 -105
- package/fesm2022/ng-forge-dynamic-forms-integration.mjs.map +1 -1
- package/fesm2022/ng-forge-dynamic-forms-internal.mjs +290 -116
- package/fesm2022/ng-forge-dynamic-forms-internal.mjs.map +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-ng-forge-dynamic-forms-ZiNaCWHl.mjs → ng-forge-dynamic-forms-ng-forge-dynamic-forms-CXQe_5td.mjs} +1871 -1632
- 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-Wbz6S8iH.mjs → ng-forge-dynamic-forms-page-field.component-DPKJcwUO.mjs} +57 -16
- 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 +7 -31
- package/types/ng-forge-dynamic-forms-internal.d.ts +669 -308
- package/types/ng-forge-dynamic-forms.d.ts +287 -33
- package/fesm2022/ng-forge-dynamic-forms-array-field.component-BVnXnaNw.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-bootstrap-derivation-orchestrator-CkqvXXs_.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-container-field.component-CM1pY9v5.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-D4JhDffB.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-ZiNaCWHl.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-page-field.component-Wbz6S8iH.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.
|
|
@@ -795,282 +1075,97 @@ type InferSingleTemplateValueBase<T, D extends number> = T extends {
|
|
|
795
1075
|
type: 'number';
|
|
796
1076
|
};
|
|
797
1077
|
} ? number : T extends {
|
|
798
|
-
type: 'checkbox' | 'toggle';
|
|
799
|
-
} ? boolean : T extends {
|
|
800
|
-
value: infer V;
|
|
801
|
-
} ? [V] extends [null] ? string : Widen<V, Depth[D]> : string;
|
|
802
|
-
type InferSingleTemplateValue<T, D extends number> = T extends {
|
|
803
|
-
nullable: true;
|
|
804
|
-
} ? InferSingleTemplateValueBase<T, D> | null : InferSingleTemplateValueBase<T, D>;
|
|
805
|
-
/**
|
|
806
|
-
* Infers the value type for an array field.
|
|
807
|
-
* Handles four cases:
|
|
808
|
-
* 1. Simplified array with template + value → infer item type from value
|
|
809
|
-
* 2. Simplified array with array template (no value) → infer object type from template fields
|
|
810
|
-
* 3. Simplified array with single-field template (no value) → infer primitive type from template field
|
|
811
|
-
* 4. Full-API array with fields → infer from children
|
|
812
|
-
*/
|
|
813
|
-
type InferArrayValue<T, D extends number> = T extends {
|
|
814
|
-
template: unknown;
|
|
815
|
-
value: readonly (infer Item)[];
|
|
816
|
-
} ? Widen<Item>[] : T extends {
|
|
817
|
-
template: infer TArr;
|
|
818
|
-
} ? TArr extends readonly RegisteredFieldTypes[] ? InferFormValueWithDepth<[...TArr], Depth[D]>[] : InferSingleTemplateValue<TArr, D>[] : T extends {
|
|
819
|
-
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
|
-
}
|
|
1078
|
+
type: 'checkbox' | 'toggle';
|
|
1079
|
+
} ? boolean : T extends {
|
|
1080
|
+
value: infer V;
|
|
1081
|
+
} ? [V] extends [null] ? string : Widen<V, Depth[D]> : string;
|
|
1082
|
+
type InferSingleTemplateValue<T, D extends number> = T extends {
|
|
1083
|
+
nullable: true;
|
|
1084
|
+
} ? InferSingleTemplateValueBase<T, D> | null : InferSingleTemplateValueBase<T, D>;
|
|
1009
1085
|
/**
|
|
1010
|
-
*
|
|
1011
|
-
*
|
|
1012
|
-
*
|
|
1086
|
+
* Infers the value type for an array field.
|
|
1087
|
+
* Handles four cases:
|
|
1088
|
+
* 1. Simplified array with template + value → infer item type from value
|
|
1089
|
+
* 2. Simplified array with array template (no value) → infer object type from template fields
|
|
1090
|
+
* 3. Simplified array with single-field template (no value) → infer primitive type from template field
|
|
1091
|
+
* 4. Full-API array with fields → infer from children
|
|
1013
1092
|
*/
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
}
|
|
1093
|
+
type InferArrayValue<T, D extends number> = T extends {
|
|
1094
|
+
template: unknown;
|
|
1095
|
+
value: readonly (infer Item)[];
|
|
1096
|
+
} ? Widen<Item>[] : T extends {
|
|
1097
|
+
template: infer TArr;
|
|
1098
|
+
} ? TArr extends readonly RegisteredFieldTypes[] ? InferFormValueWithDepth<[...TArr], Depth[D]>[] : InferSingleTemplateValue<TArr, D>[] : T extends {
|
|
1099
|
+
fields: infer F;
|
|
1100
|
+
} ? InferContainerChildren<F, Depth[D], unknown>[] : unknown[];
|
|
1020
1101
|
/**
|
|
1021
|
-
*
|
|
1022
|
-
*
|
|
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.
|
|
1023
1109
|
*/
|
|
1024
|
-
type
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
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;
|
|
1035
1148
|
/**
|
|
1036
|
-
*
|
|
1037
|
-
*
|
|
1038
|
-
*
|
|
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.
|
|
1039
1152
|
*/
|
|
1040
|
-
|
|
1041
|
-
/** Validator type identifier. */
|
|
1042
|
-
type: 'http';
|
|
1043
|
-
/** Optional parameters to pass to HTTP validator */
|
|
1044
|
-
params?: Record<string, unknown>;
|
|
1045
|
-
}
|
|
1046
|
-
/** Function-based HTTP validator configuration. Uses Angular's `validateHttp` API. */
|
|
1047
|
-
type FunctionHttpValidatorConfig = (FunctionHttpValidatorConfigShared & {
|
|
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
|
-
}
|
|
1153
|
+
type InferFormValue<T> = ExtractFields<T> extends readonly RegisteredFieldTypes[] ? InferFormValueWithDepth<ExtractFields<T> extends RegisteredFieldTypes[] ? ExtractFields<T> : [...ExtractFields<T>], 5> : never;
|
|
1067
1154
|
/**
|
|
1068
|
-
*
|
|
1069
|
-
*
|
|
1155
|
+
* Default `TModel` for `DynamicForm` / `FormStateManager` generics.
|
|
1156
|
+
*
|
|
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.
|
|
@@ -1584,11 +1656,28 @@ interface FormOptions {
|
|
|
1584
1656
|
/** Default disabled behavior for next page buttons. */
|
|
1585
1657
|
nextButton?: NextButtonOptions;
|
|
1586
1658
|
/**
|
|
1587
|
-
* For paged forms:
|
|
1588
|
-
*
|
|
1589
|
-
*
|
|
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.
|
|
1590
1679
|
*
|
|
1591
|
-
* `0` mounts only the current page; `1`
|
|
1680
|
+
* `0` mounts only the current page; `1` preloads ±1 on idle for flicker-free sequential
|
|
1592
1681
|
* navigation; higher values pre-warm more pages for jump navigation at the cost
|
|
1593
1682
|
* of more initial DOM + change detection.
|
|
1594
1683
|
*
|
|
@@ -1604,7 +1693,16 @@ interface FormOptions {
|
|
|
1604
1693
|
*
|
|
1605
1694
|
* `true` enables windowing using the global `withFieldWindowing()` defaults;
|
|
1606
1695
|
* `false` force-disables it even if the global feature is enabled; an object
|
|
1607
|
-
* enables it with per-form `eager` / `placeholderHeight` overrides.
|
|
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.
|
|
1608
1706
|
*
|
|
1609
1707
|
* Overrides the global `withFieldWindowing(...)` default for this form.
|
|
1610
1708
|
*
|
|
@@ -1613,6 +1711,9 @@ interface FormOptions {
|
|
|
1613
1711
|
fieldWindowing?: boolean | {
|
|
1614
1712
|
eager?: number;
|
|
1615
1713
|
placeholderHeight?: string;
|
|
1714
|
+
park?: boolean | {
|
|
1715
|
+
margin?: string;
|
|
1716
|
+
};
|
|
1616
1717
|
};
|
|
1617
1718
|
/**
|
|
1618
1719
|
* Whether to exclude values of hidden fields from submission output.
|
|
@@ -1644,6 +1745,67 @@ interface FormOptions {
|
|
|
1644
1745
|
* @default undefined (uses global setting)
|
|
1645
1746
|
*/
|
|
1646
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';
|
|
1647
1809
|
}
|
|
1648
1810
|
/** Options for controlling submit button disabled behavior. */
|
|
1649
1811
|
interface SubmitButtonOptions {
|
|
@@ -1660,6 +1822,18 @@ interface SubmitButtonOptions {
|
|
|
1660
1822
|
*/
|
|
1661
1823
|
disableWhileSubmitting?: boolean;
|
|
1662
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
|
+
}
|
|
1663
1837
|
/** Options for controlling next page button disabled behavior. */
|
|
1664
1838
|
interface NextButtonOptions {
|
|
1665
1839
|
/**
|
|
@@ -1821,6 +1995,24 @@ interface RowWrapper {
|
|
|
1821
1995
|
readonly type: 'row';
|
|
1822
1996
|
}
|
|
1823
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
|
+
|
|
1824
2016
|
/** Container fields registry - augment this interface to add custom container fields */
|
|
1825
2017
|
interface FieldRegistryContainers {
|
|
1826
2018
|
page: PageField;
|
|
@@ -1838,6 +2030,7 @@ interface FieldRegistryLeaves {
|
|
|
1838
2030
|
interface FieldRegistryWrappers {
|
|
1839
2031
|
css: CssWrapper;
|
|
1840
2032
|
row: RowWrapper;
|
|
2033
|
+
'field-errors': FieldErrorsWrapper;
|
|
1841
2034
|
}
|
|
1842
2035
|
/**
|
|
1843
2036
|
* Global interface for dynamic form field definitions with categorization
|
|
@@ -1916,6 +2109,12 @@ interface WrapperTypeDefinition<T extends WrapperConfig = WrapperConfig> {
|
|
|
1916
2109
|
loadComponent: LazyComponentLoader;
|
|
1917
2110
|
/** Field types this wrapper should auto-apply to. */
|
|
1918
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;
|
|
1919
2118
|
}
|
|
1920
2119
|
/** Type guard for WrapperTypeDefinition. */
|
|
1921
2120
|
declare function isWrapperTypeDefinition(value: unknown): value is WrapperTypeDefinition;
|
|
@@ -1941,9 +2140,11 @@ interface FieldWrapper {
|
|
|
1941
2140
|
readonly fieldComponent: Signal<ViewContainerRef>;
|
|
1942
2141
|
}
|
|
1943
2142
|
/** Injection token for the wrapper type registry. */
|
|
1944
|
-
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>>>;
|
|
1945
2144
|
/** Component cache for loaded wrapper components. */
|
|
1946
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>>>;
|
|
1947
2148
|
/**
|
|
1948
2149
|
* Pre-computed reverse index: `fieldType → WrapperConfig[]` for every
|
|
1949
2150
|
* registered `WrapperTypeDefinition.types` entry. Built once in the
|
|
@@ -2919,8 +3120,122 @@ declare class RootFormRegistryService {
|
|
|
2919
3120
|
constructor(formValue: Signal<Record<string, unknown>>, rootForm: Signal<FieldTree<Record<string, unknown>> | undefined>);
|
|
2920
3121
|
}
|
|
2921
3122
|
|
|
2922
|
-
|
|
2923
|
-
|
|
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;
|
|
2924
3239
|
|
|
2925
3240
|
/**
|
|
2926
3241
|
* Create a dynamic value function from an expression string.
|
|
@@ -2969,7 +3284,7 @@ interface BaseCheckedField<TProps, TMeta extends FieldMeta = FieldMeta, TNullabl
|
|
|
2969
3284
|
nullable?: TNullable;
|
|
2970
3285
|
}
|
|
2971
3286
|
declare function isCheckedField<TProps, TMeta extends FieldMeta = FieldMeta>(field: FieldDef<TProps, TMeta>): field is BaseCheckedField<TProps, TMeta, boolean>;
|
|
2972
|
-
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';
|
|
2973
3288
|
type CheckedFieldComponent<T extends BaseCheckedField<Record<string, unknown> | unknown, FieldMeta, boolean>> = Prettify<WithInputSignals<Omit<T, ExcludedKeys$1>>>;
|
|
2974
3289
|
|
|
2975
3290
|
/**
|
|
@@ -2993,7 +3308,7 @@ interface BaseValueField<TProps, TValue, TMeta extends FieldMeta = FieldMeta, TN
|
|
|
2993
3308
|
nullable?: TNullable;
|
|
2994
3309
|
}
|
|
2995
3310
|
declare function isValueField<TProps, TMeta extends FieldMeta = FieldMeta>(field: FieldDef<TProps, TMeta>): field is BaseValueField<TProps, ValueType, TMeta, boolean>;
|
|
2996
|
-
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';
|
|
2997
3312
|
type ValueFieldComponent<T extends BaseValueField<Record<string, unknown> | unknown, unknown, FieldMeta, boolean>> = Prettify<WithInputSignals<Omit<T, ExcludedKeys>>>;
|
|
2998
3313
|
|
|
2999
3314
|
/** Base interface for all form events in the dynamic form system. */
|
|
@@ -3637,6 +3952,11 @@ declare function memoize<TFunc extends (...args: any[]) => any>(fn: TFunc, resol
|
|
|
3637
3952
|
* @returns Fields as array
|
|
3638
3953
|
*/
|
|
3639
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>;
|
|
3640
3960
|
/**
|
|
3641
3961
|
* Gets the keys that differ between two objects.
|
|
3642
3962
|
*
|
|
@@ -3656,5 +3976,46 @@ declare function assertNever(value: never): never;
|
|
|
3656
3976
|
*/
|
|
3657
3977
|
declare function withPreviousValue<T>(input: Resource<T>): Resource<T>;
|
|
3658
3978
|
|
|
3659
|
-
|
|
3660
|
-
|
|
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 };
|