@ng-forge/dynamic-forms 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/ng-forge-dynamic-forms-integration.mjs +48 -4
- package/fesm2022/ng-forge-dynamic-forms-integration.mjs.map +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-ng-forge-dynamic-forms-B_676NHl.mjs → ng-forge-dynamic-forms-ng-forge-dynamic-forms-CZBdtTAU.mjs} +302 -301
- package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-CZBdtTAU.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-page-field.component-YHFQcpCS.mjs → ng-forge-dynamic-forms-page-field.component-BM32StxL.mjs} +6 -6
- package/fesm2022/ng-forge-dynamic-forms-page-field.component-BM32StxL.mjs.map +1 -0
- package/fesm2022/{ng-forge-dynamic-forms-testing-test-button.harness-BenmCFcM.mjs → ng-forge-dynamic-forms-testing-test-button.harness-CbctXjtc.mjs} +4 -4
- package/fesm2022/{ng-forge-dynamic-forms-testing-test-button.harness-BenmCFcM.mjs.map → ng-forge-dynamic-forms-testing-test-button.harness-CbctXjtc.mjs.map} +1 -1
- package/fesm2022/ng-forge-dynamic-forms-testing.mjs +32 -29
- package/fesm2022/ng-forge-dynamic-forms-testing.mjs.map +1 -1
- package/fesm2022/{ng-forge-dynamic-forms-text-field.component-CiYq73aF.mjs → ng-forge-dynamic-forms-text-field.component-C0I8jf9T.mjs} +5 -5
- package/fesm2022/{ng-forge-dynamic-forms-text-field.component-CiYq73aF.mjs.map → ng-forge-dynamic-forms-text-field.component-C0I8jf9T.mjs.map} +1 -1
- package/fesm2022/ng-forge-dynamic-forms.mjs +1 -1
- package/package.json +4 -4
- package/types/ng-forge-dynamic-forms-integration.d.ts +14 -2
- package/types/ng-forge-dynamic-forms-testing.d.ts +35 -34
- package/types/ng-forge-dynamic-forms.d.ts +66 -89
- package/fesm2022/ng-forge-dynamic-forms-ng-forge-dynamic-forms-B_676NHl.mjs.map +0 -1
- package/fesm2022/ng-forge-dynamic-forms-page-field.component-YHFQcpCS.mjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { isSignal, computed, runInInjectionContext, InjectionToken, inject,
|
|
2
|
+
import { isSignal, computed, runInInjectionContext, InjectionToken, inject, signal, Injectable, untracked, input, linkedSignal, ChangeDetectionStrategy, Component, DestroyRef, Injector, model, afterNextRender, makeEnvironmentProviders, Pipe } from '@angular/core';
|
|
3
3
|
import { NgComponentOutlet } from '@angular/common';
|
|
4
4
|
import { submit, email, min, max, minLength, maxLength, pattern, required, validate, validateAsync, validateHttp, disabled, readonly, hidden, applyEach, applyWhenValue, applyWhen, apply, schema, validateTree, form } from '@angular/forms/signals';
|
|
5
5
|
import { toObservable, toSignal, takeUntilDestroyed, outputFromObservable } from '@angular/core/rxjs-interop';
|
|
@@ -73,27 +73,12 @@ function derivedFromDeferred(source, operator, options) {
|
|
|
73
73
|
* // Returns: ''
|
|
74
74
|
* ```
|
|
75
75
|
*
|
|
76
|
-
* @example
|
|
77
|
-
* ```typescript
|
|
78
|
-
* // Usage in component template
|
|
79
|
-
* @Component({
|
|
80
|
-
* template: `
|
|
81
|
-
* <div [class]="getGridClassString(field)" class="form-field">
|
|
82
|
-
* <!-- Field content -->
|
|
83
|
-
* </div>
|
|
84
|
-
* `
|
|
85
|
-
* })
|
|
86
|
-
* export class FieldComponent {
|
|
87
|
-
* field: FieldDef<unknown>;
|
|
88
|
-
* getGridClassString = getGridClassString;
|
|
89
|
-
* }
|
|
90
|
-
* ```
|
|
91
|
-
*
|
|
92
76
|
* @public
|
|
93
77
|
*/
|
|
94
78
|
function getGridClassString(fieldDef) {
|
|
95
|
-
|
|
96
|
-
|
|
79
|
+
const col = fieldDef.col;
|
|
80
|
+
if (typeof col === 'number' && Number.isInteger(col) && col > 0 && col <= 12) {
|
|
81
|
+
return `df-col-${col}`;
|
|
97
82
|
}
|
|
98
83
|
return '';
|
|
99
84
|
}
|
|
@@ -248,7 +233,7 @@ function createFieldResolutionPipe(getContext) {
|
|
|
248
233
|
* @param action - The submission action function
|
|
249
234
|
* @returns A wrapped function that always returns a Promise
|
|
250
235
|
*/
|
|
251
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
236
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Form tree type is dynamic, action signatures vary
|
|
252
237
|
function wrapSubmissionAction(action) {
|
|
253
238
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
254
239
|
return (formTree) => {
|
|
@@ -399,11 +384,11 @@ function memoize(fn, resolverOrOptions) {
|
|
|
399
384
|
const cache = new Map();
|
|
400
385
|
return ((...args) => {
|
|
401
386
|
const key = resolver ? resolver(...args) : JSON.stringify(args);
|
|
402
|
-
|
|
403
|
-
|
|
387
|
+
const cachedValue = cache.get(key);
|
|
388
|
+
if (cachedValue !== undefined) {
|
|
404
389
|
cache.delete(key);
|
|
405
|
-
cache.set(key,
|
|
406
|
-
return
|
|
390
|
+
cache.set(key, cachedValue);
|
|
391
|
+
return cachedValue;
|
|
407
392
|
}
|
|
408
393
|
const result = fn(...args);
|
|
409
394
|
if (cache.size >= maxSize) {
|
|
@@ -534,6 +519,21 @@ const FIELD_SIGNAL_CONTEXT = new InjectionToken('FIELD_SIGNAL_CONTEXT', {
|
|
|
534
519
|
*/
|
|
535
520
|
const ARRAY_CONTEXT = new InjectionToken('ARRAY_CONTEXT');
|
|
536
521
|
|
|
522
|
+
/**
|
|
523
|
+
* Base error class for all Dynamic Forms errors.
|
|
524
|
+
*
|
|
525
|
+
* This class centralizes the `[Dynamic Forms]` prefix, ensuring consistent
|
|
526
|
+
* error messaging across the library without requiring each error site to
|
|
527
|
+
* manually include the prefix.
|
|
528
|
+
*/
|
|
529
|
+
class DynamicFormError extends Error {
|
|
530
|
+
static PREFIX = '[Dynamic Forms]';
|
|
531
|
+
constructor(message) {
|
|
532
|
+
super(`${DynamicFormError.PREFIX} ${message}`);
|
|
533
|
+
this.name = 'DynamicFormError';
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
537
|
/**
|
|
538
538
|
* Injection token for the global field type registry.
|
|
539
539
|
*
|
|
@@ -663,20 +663,21 @@ function injectFieldRegistry() {
|
|
|
663
663
|
async loadTypeComponent(name) {
|
|
664
664
|
const fieldType = registry.get(name);
|
|
665
665
|
if (!fieldType) {
|
|
666
|
-
throw new
|
|
666
|
+
throw new DynamicFormError(`Field type "${name}" is not registered`);
|
|
667
667
|
}
|
|
668
668
|
// Handle async loading
|
|
669
669
|
if (fieldType.loadComponent) {
|
|
670
670
|
try {
|
|
671
671
|
const result = await fieldType.loadComponent();
|
|
672
672
|
// Handle ES module imports - extract default export if needed
|
|
673
|
-
|
|
673
|
+
const moduleResult = result;
|
|
674
|
+
return (typeof moduleResult === 'object' && 'default' in moduleResult && moduleResult.default) || result;
|
|
674
675
|
}
|
|
675
676
|
catch (error) {
|
|
676
|
-
throw new
|
|
677
|
+
throw new DynamicFormError(`Failed to load component for field type "${name}": ${error}`);
|
|
677
678
|
}
|
|
678
679
|
}
|
|
679
|
-
throw new
|
|
680
|
+
throw new DynamicFormError(`Field type "${name}" has no component or loadComponent function`);
|
|
680
681
|
},
|
|
681
682
|
/**
|
|
682
683
|
* Gets all registered field type definitions.
|
|
@@ -735,100 +736,13 @@ function injectFieldRegistry() {
|
|
|
735
736
|
};
|
|
736
737
|
}
|
|
737
738
|
|
|
738
|
-
/**
|
|
739
|
-
* Log levels for the dynamic forms logger.
|
|
740
|
-
* Each level includes all levels above it (more severe).
|
|
741
|
-
*
|
|
742
|
-
* - Off: No logging
|
|
743
|
-
* - Error: Only errors
|
|
744
|
-
* - Warn: Errors + warnings
|
|
745
|
-
* - Info: Errors + warnings + info
|
|
746
|
-
* - Debug: All messages including debug
|
|
747
|
-
*/
|
|
748
|
-
var LogLevel;
|
|
749
|
-
(function (LogLevel) {
|
|
750
|
-
/** Disable all logging */
|
|
751
|
-
LogLevel[LogLevel["Off"] = 0] = "Off";
|
|
752
|
-
/** Only log errors */
|
|
753
|
-
LogLevel[LogLevel["Error"] = 1] = "Error";
|
|
754
|
-
/** Log errors and warnings */
|
|
755
|
-
LogLevel[LogLevel["Warn"] = 2] = "Warn";
|
|
756
|
-
/** Log errors, warnings, and info messages */
|
|
757
|
-
LogLevel[LogLevel["Info"] = 3] = "Info";
|
|
758
|
-
/** Log all messages including debug */
|
|
759
|
-
LogLevel[LogLevel["Debug"] = 4] = "Debug";
|
|
760
|
-
})(LogLevel || (LogLevel = {}));
|
|
761
|
-
|
|
762
|
-
/**
|
|
763
|
-
* Console-based logger implementation.
|
|
764
|
-
* Respects log level configuration to filter messages.
|
|
765
|
-
*/
|
|
766
|
-
class ConsoleLogger {
|
|
767
|
-
level;
|
|
768
|
-
prefix = '[Dynamic Forms]';
|
|
769
|
-
constructor(level) {
|
|
770
|
-
this.level = level;
|
|
771
|
-
}
|
|
772
|
-
debug(message, ...args) {
|
|
773
|
-
if (this.level >= LogLevel.Debug) {
|
|
774
|
-
console.debug(this.prefix, message, ...args);
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
info(message, ...args) {
|
|
778
|
-
if (this.level >= LogLevel.Info) {
|
|
779
|
-
console.info(this.prefix, message, ...args);
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
warn(message, ...args) {
|
|
783
|
-
if (this.level >= LogLevel.Warn) {
|
|
784
|
-
console.warn(this.prefix, message, ...args);
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
error(message, ...args) {
|
|
788
|
-
if (this.level >= LogLevel.Error) {
|
|
789
|
-
console.error(this.prefix, message, ...args);
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
/**
|
|
795
|
-
* No-operation logger implementation.
|
|
796
|
-
* All methods are no-ops - used in production by default.
|
|
797
|
-
*/
|
|
798
|
-
class NoopLogger {
|
|
799
|
-
debug() {
|
|
800
|
-
// Intentionally empty
|
|
801
|
-
}
|
|
802
|
-
info() {
|
|
803
|
-
// Intentionally empty
|
|
804
|
-
}
|
|
805
|
-
warn() {
|
|
806
|
-
// Intentionally empty
|
|
807
|
-
}
|
|
808
|
-
error() {
|
|
809
|
-
// Intentionally empty
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
|
|
813
739
|
/**
|
|
814
740
|
* Injection token for the dynamic forms logger.
|
|
815
741
|
*
|
|
816
|
-
*
|
|
817
|
-
*
|
|
818
|
-
* - Production mode: NoopLogger (silent)
|
|
819
|
-
*
|
|
820
|
-
* Override by providing a custom logger via withLogger() feature
|
|
821
|
-
* or by providing directly to this token.
|
|
742
|
+
* Provided by provideDynamicForm() with ConsoleLogger as default.
|
|
743
|
+
* Override with withLoggerConfig(false) to disable logging.
|
|
822
744
|
*/
|
|
823
|
-
const
|
|
824
|
-
providedIn: 'root',
|
|
825
|
-
factory: () => {
|
|
826
|
-
if (isDevMode()) {
|
|
827
|
-
return new ConsoleLogger(LogLevel.Warn);
|
|
828
|
-
}
|
|
829
|
-
return new NoopLogger();
|
|
830
|
-
},
|
|
831
|
-
});
|
|
745
|
+
const DynamicFormLogger = new InjectionToken('DynamicFormLogger');
|
|
832
746
|
|
|
833
747
|
/**
|
|
834
748
|
* Registry service that maintains references to root form fields.
|
|
@@ -987,10 +901,10 @@ class RootFormRegistryService {
|
|
|
987
901
|
this._formValueSignals.set(new Map());
|
|
988
902
|
this.formContexts.clear();
|
|
989
903
|
}
|
|
990
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
991
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.
|
|
904
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: RootFormRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
905
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: RootFormRegistryService });
|
|
992
906
|
}
|
|
993
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
907
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: RootFormRegistryService, decorators: [{
|
|
994
908
|
type: Injectable
|
|
995
909
|
}] });
|
|
996
910
|
|
|
@@ -1006,7 +920,7 @@ function isChildFieldContext(context) {
|
|
|
1006
920
|
*/
|
|
1007
921
|
class FieldContextRegistryService {
|
|
1008
922
|
rootFormRegistry = inject(RootFormRegistryService);
|
|
1009
|
-
logger = inject(
|
|
923
|
+
logger = inject(DynamicFormLogger);
|
|
1010
924
|
/**
|
|
1011
925
|
* Creates an evaluation context for a field by combining:
|
|
1012
926
|
* - The field's current value
|
|
@@ -1114,10 +1028,10 @@ class FieldContextRegistryService {
|
|
|
1114
1028
|
logger: this.logger,
|
|
1115
1029
|
};
|
|
1116
1030
|
}
|
|
1117
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
1118
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.
|
|
1031
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FieldContextRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1032
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FieldContextRegistryService });
|
|
1119
1033
|
}
|
|
1120
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
1034
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FieldContextRegistryService, decorators: [{
|
|
1121
1035
|
type: Injectable
|
|
1122
1036
|
}] });
|
|
1123
1037
|
|
|
@@ -1457,10 +1371,10 @@ class FunctionRegistryService {
|
|
|
1457
1371
|
this.clearAsyncValidators();
|
|
1458
1372
|
this.clearHttpValidators();
|
|
1459
1373
|
}
|
|
1460
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
1461
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.
|
|
1374
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FunctionRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1375
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FunctionRegistryService });
|
|
1462
1376
|
}
|
|
1463
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
1377
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: FunctionRegistryService, decorators: [{
|
|
1464
1378
|
type: Injectable
|
|
1465
1379
|
}] });
|
|
1466
1380
|
|
|
@@ -1607,7 +1521,7 @@ var TokenType;
|
|
|
1607
1521
|
/**
|
|
1608
1522
|
* Parser error with position information
|
|
1609
1523
|
*/
|
|
1610
|
-
class ExpressionParserError extends
|
|
1524
|
+
class ExpressionParserError extends DynamicFormError {
|
|
1611
1525
|
position;
|
|
1612
1526
|
expression;
|
|
1613
1527
|
constructor(message, position, expression) {
|
|
@@ -1705,7 +1619,7 @@ class Tokenizer {
|
|
|
1705
1619
|
this.position++;
|
|
1706
1620
|
}
|
|
1707
1621
|
}
|
|
1708
|
-
throw new ExpressionParserError('
|
|
1622
|
+
throw new ExpressionParserError('Unterminated string literal', start, this.expression);
|
|
1709
1623
|
}
|
|
1710
1624
|
readNumber() {
|
|
1711
1625
|
const start = this.position;
|
|
@@ -1810,7 +1724,7 @@ class Tokenizer {
|
|
|
1810
1724
|
case ']':
|
|
1811
1725
|
return { type: TokenType.RBRACKET, value: ']', position: start };
|
|
1812
1726
|
default:
|
|
1813
|
-
throw new ExpressionParserError(`
|
|
1727
|
+
throw new ExpressionParserError(`Unexpected character: ${char}`, start, this.expression);
|
|
1814
1728
|
}
|
|
1815
1729
|
}
|
|
1816
1730
|
}
|
|
@@ -1834,7 +1748,7 @@ class Parser {
|
|
|
1834
1748
|
this.tokens = tokenizer.tokenize();
|
|
1835
1749
|
this.current = 0;
|
|
1836
1750
|
if (this.isAtEnd()) {
|
|
1837
|
-
throw new ExpressionParserError('
|
|
1751
|
+
throw new ExpressionParserError('Empty expression', 0, this.expression);
|
|
1838
1752
|
}
|
|
1839
1753
|
return this.parseExpression();
|
|
1840
1754
|
}
|
|
@@ -1909,7 +1823,7 @@ class Parser {
|
|
|
1909
1823
|
if (this.match(TokenType.DOT)) {
|
|
1910
1824
|
// Member access: obj.property
|
|
1911
1825
|
if (!this.check(TokenType.IDENTIFIER)) {
|
|
1912
|
-
throw new ExpressionParserError('
|
|
1826
|
+
throw new ExpressionParserError('Expected property name after "."', this.peek().position, this.expression);
|
|
1913
1827
|
}
|
|
1914
1828
|
const property = this.advance().value;
|
|
1915
1829
|
node = { type: 'MemberAccess', object: node, property };
|
|
@@ -1962,7 +1876,7 @@ class Parser {
|
|
|
1962
1876
|
this.consume(TokenType.RPAREN, 'Expected ")" after expression');
|
|
1963
1877
|
return expr;
|
|
1964
1878
|
}
|
|
1965
|
-
throw new ExpressionParserError(`
|
|
1879
|
+
throw new ExpressionParserError(`Unexpected token: ${this.peek().value}`, this.peek().position, this.expression);
|
|
1966
1880
|
}
|
|
1967
1881
|
parseArgumentList() {
|
|
1968
1882
|
const args = [];
|
|
@@ -2013,7 +1927,7 @@ class Parser {
|
|
|
2013
1927
|
consume(type, message) {
|
|
2014
1928
|
if (this.check(type))
|
|
2015
1929
|
return this.advance();
|
|
2016
|
-
throw new ExpressionParserError(
|
|
1930
|
+
throw new ExpressionParserError(message, this.peek().position, this.expression);
|
|
2017
1931
|
}
|
|
2018
1932
|
}
|
|
2019
1933
|
|
|
@@ -2165,7 +2079,7 @@ class Evaluator {
|
|
|
2165
2079
|
case 'ArrayLiteral':
|
|
2166
2080
|
return this.evaluateArrayLiteral(node);
|
|
2167
2081
|
default:
|
|
2168
|
-
throw new ExpressionParserError(`
|
|
2082
|
+
throw new ExpressionParserError(`Unknown node type: ${node.type}`, 0, this.expression);
|
|
2169
2083
|
}
|
|
2170
2084
|
}
|
|
2171
2085
|
evaluateIdentifier(name) {
|
|
@@ -2181,7 +2095,7 @@ class Evaluator {
|
|
|
2181
2095
|
}
|
|
2182
2096
|
// Block access to dangerous properties that could leak information or enable attacks
|
|
2183
2097
|
if (BLOCKED_PROPERTIES.has(node.property)) {
|
|
2184
|
-
throw new ExpressionParserError(`
|
|
2098
|
+
throw new ExpressionParserError(`Property "${node.property}" is not accessible for security reasons`, 0, this.expression);
|
|
2185
2099
|
}
|
|
2186
2100
|
// Allow property access on plain objects and primitives
|
|
2187
2101
|
if (typeof obj === 'object' || typeof obj === 'string' || typeof obj === 'number') {
|
|
@@ -2236,7 +2150,7 @@ class Evaluator {
|
|
|
2236
2150
|
case '<=':
|
|
2237
2151
|
return left <= right;
|
|
2238
2152
|
default:
|
|
2239
|
-
throw new ExpressionParserError(`
|
|
2153
|
+
throw new ExpressionParserError(`Unknown binary operator: ${node.operator}`, 0, this.expression);
|
|
2240
2154
|
}
|
|
2241
2155
|
}
|
|
2242
2156
|
evaluateUnaryOp(node) {
|
|
@@ -2249,23 +2163,23 @@ class Evaluator {
|
|
|
2249
2163
|
case '+':
|
|
2250
2164
|
return +operand;
|
|
2251
2165
|
default:
|
|
2252
|
-
throw new ExpressionParserError(`
|
|
2166
|
+
throw new ExpressionParserError(`Unknown unary operator: ${node.operator}`, 0, this.expression);
|
|
2253
2167
|
}
|
|
2254
2168
|
}
|
|
2255
2169
|
evaluateCallExpression(node) {
|
|
2256
2170
|
// Only allow method calls (member access), not arbitrary function calls
|
|
2257
2171
|
if (node.callee.type !== 'MemberAccess') {
|
|
2258
|
-
throw new ExpressionParserError('
|
|
2172
|
+
throw new ExpressionParserError('Only method calls are allowed, not arbitrary function calls', 0, this.expression);
|
|
2259
2173
|
}
|
|
2260
2174
|
const obj = this.evaluate(node.callee.object);
|
|
2261
2175
|
const methodName = node.callee.property;
|
|
2262
2176
|
// Block access to dangerous properties (must check before method call)
|
|
2263
2177
|
if (BLOCKED_PROPERTIES.has(methodName)) {
|
|
2264
|
-
throw new ExpressionParserError(`
|
|
2178
|
+
throw new ExpressionParserError(`Property "${methodName}" is not accessible for security reasons`, 0, this.expression);
|
|
2265
2179
|
}
|
|
2266
2180
|
// Check if the method is in the instance method whitelist
|
|
2267
2181
|
if (!this.isMethodSafe(obj, methodName)) {
|
|
2268
|
-
throw new ExpressionParserError(`
|
|
2182
|
+
throw new ExpressionParserError(`Method "${methodName}" is not allowed for security reasons`, 0, this.expression);
|
|
2269
2183
|
}
|
|
2270
2184
|
// Evaluate arguments
|
|
2271
2185
|
const args = node.arguments.map((arg) => this.evaluate(arg));
|
|
@@ -2383,7 +2297,7 @@ class ExpressionParser {
|
|
|
2383
2297
|
throw error;
|
|
2384
2298
|
}
|
|
2385
2299
|
// Wrap other errors for consistency
|
|
2386
|
-
throw new ExpressionParserError(`
|
|
2300
|
+
throw new ExpressionParserError(`Error evaluating expression: ${error instanceof Error ? error.message : String(error)}`, 0, expression);
|
|
2387
2301
|
}
|
|
2388
2302
|
}
|
|
2389
2303
|
/**
|
|
@@ -2478,6 +2392,56 @@ function evaluateOrCondition(expression, context) {
|
|
|
2478
2392
|
return expression.conditions.some((condition) => evaluateCondition(condition, context));
|
|
2479
2393
|
}
|
|
2480
2394
|
|
|
2395
|
+
/**
|
|
2396
|
+
* Cache for memoized logic functions, keyed by injection context.
|
|
2397
|
+
* Uses WeakMap with FunctionRegistryService as outer key to automatically clean up.
|
|
2398
|
+
* Inner structure: Map<FieldContextRegistryService, Map<expressionKey, LogicFn>>
|
|
2399
|
+
*/
|
|
2400
|
+
const logicFunctionCache = new WeakMap();
|
|
2401
|
+
/**
|
|
2402
|
+
* Serializes a value to a deterministic string for cache key generation.
|
|
2403
|
+
* Unlike JSON.stringify, this sorts object keys to ensure consistent output
|
|
2404
|
+
* regardless of property insertion order.
|
|
2405
|
+
*/
|
|
2406
|
+
function stableStringify(value) {
|
|
2407
|
+
if (value === null || value === undefined) {
|
|
2408
|
+
return String(value);
|
|
2409
|
+
}
|
|
2410
|
+
if (typeof value !== 'object') {
|
|
2411
|
+
return JSON.stringify(value);
|
|
2412
|
+
}
|
|
2413
|
+
if (Array.isArray(value)) {
|
|
2414
|
+
return '[' + value.map(stableStringify).join(',') + ']';
|
|
2415
|
+
}
|
|
2416
|
+
const obj = value;
|
|
2417
|
+
const sortedKeys = Object.keys(obj).sort();
|
|
2418
|
+
const pairs = sortedKeys.map((key) => JSON.stringify(key) + ':' + stableStringify(obj[key]));
|
|
2419
|
+
return '{' + pairs.join(',') + '}';
|
|
2420
|
+
}
|
|
2421
|
+
/**
|
|
2422
|
+
* Serializes a conditional expression to a deterministic string key for caching.
|
|
2423
|
+
* Uses stable stringification to ensure equivalent objects produce the same key
|
|
2424
|
+
* regardless of property order.
|
|
2425
|
+
*/
|
|
2426
|
+
function serializeExpression(expression) {
|
|
2427
|
+
return stableStringify(expression);
|
|
2428
|
+
}
|
|
2429
|
+
/**
|
|
2430
|
+
* Gets or creates the cache map for a specific injection context.
|
|
2431
|
+
*/
|
|
2432
|
+
function getContextCache(ctx) {
|
|
2433
|
+
let outerCache = logicFunctionCache.get(ctx.functionRegistry);
|
|
2434
|
+
if (!outerCache) {
|
|
2435
|
+
outerCache = new WeakMap();
|
|
2436
|
+
logicFunctionCache.set(ctx.functionRegistry, outerCache);
|
|
2437
|
+
}
|
|
2438
|
+
let innerCache = outerCache.get(ctx.fieldContextRegistry);
|
|
2439
|
+
if (!innerCache) {
|
|
2440
|
+
innerCache = new Map();
|
|
2441
|
+
outerCache.set(ctx.fieldContextRegistry, innerCache);
|
|
2442
|
+
}
|
|
2443
|
+
return innerCache;
|
|
2444
|
+
}
|
|
2481
2445
|
/**
|
|
2482
2446
|
* Create a logic function from a conditional expression.
|
|
2483
2447
|
*
|
|
@@ -2489,17 +2453,32 @@ function evaluateOrCondition(expression, context) {
|
|
|
2489
2453
|
* NOTE: For validators, use createEvaluationContext directly (with untracked) to prevent
|
|
2490
2454
|
* infinite reactive loops. Validators with cross-field dependencies should be hoisted
|
|
2491
2455
|
* to form-level using validateTree.
|
|
2456
|
+
*
|
|
2457
|
+
* @param expression The conditional expression to evaluate
|
|
2458
|
+
* @returns A LogicFn that evaluates the condition in the context of a field
|
|
2492
2459
|
*/
|
|
2493
2460
|
function createLogicFunction(expression) {
|
|
2494
2461
|
// Inject services during factory creation, not during execution
|
|
2495
2462
|
const functionRegistry = inject(FunctionRegistryService);
|
|
2496
2463
|
const fieldContextRegistry = inject(FieldContextRegistryService);
|
|
2497
|
-
|
|
2464
|
+
// Get cache for this injection context
|
|
2465
|
+
const contextCache = getContextCache({ functionRegistry, fieldContextRegistry });
|
|
2466
|
+
// Generate cache key from serialized expression
|
|
2467
|
+
const cacheKey = serializeExpression(expression);
|
|
2468
|
+
// Check cache first
|
|
2469
|
+
const cached = contextCache.get(cacheKey);
|
|
2470
|
+
if (cached) {
|
|
2471
|
+
return cached;
|
|
2472
|
+
}
|
|
2473
|
+
const fn = (ctx) => {
|
|
2498
2474
|
// Create REACTIVE evaluation context for logic functions
|
|
2499
2475
|
// This allows logic to re-evaluate when dependent fields change
|
|
2500
2476
|
const evaluationContext = fieldContextRegistry.createReactiveEvaluationContext(ctx, functionRegistry.getCustomFunctions());
|
|
2501
2477
|
return evaluateCondition(expression, evaluationContext);
|
|
2502
2478
|
};
|
|
2479
|
+
// Cache the function
|
|
2480
|
+
contextCache.set(cacheKey, fn);
|
|
2481
|
+
return fn;
|
|
2503
2482
|
}
|
|
2504
2483
|
|
|
2505
2484
|
/**
|
|
@@ -2512,11 +2491,6 @@ const dynamicValueFunctionCache = new WeakMap();
|
|
|
2512
2491
|
* Create a dynamic value function from an expression string.
|
|
2513
2492
|
* Uses secure AST-based parsing instead of eval() or new Function().
|
|
2514
2493
|
*
|
|
2515
|
-
* Optimizations:
|
|
2516
|
-
* - Injects FieldContextRegistryService at creation time, not execution time
|
|
2517
|
-
* - Caches created functions by expression string per injection context
|
|
2518
|
-
* - Expression parsing is already cached by ExpressionParser's LRU cache
|
|
2519
|
-
*
|
|
2520
2494
|
* @param expression The expression string to evaluate
|
|
2521
2495
|
* @returns A LogicFn that evaluates the expression in the context of a field
|
|
2522
2496
|
*/
|
|
@@ -2524,7 +2498,7 @@ function createDynamicValueFunction(expression) {
|
|
|
2524
2498
|
// Inject services during factory creation, not during execution
|
|
2525
2499
|
// This captures the service instance in the closure
|
|
2526
2500
|
const fieldContextRegistry = inject(FieldContextRegistryService);
|
|
2527
|
-
const logger = inject(
|
|
2501
|
+
const logger = inject(DynamicFormLogger);
|
|
2528
2502
|
// Get or create cache for this injection context
|
|
2529
2503
|
let contextCache = dynamicValueFunctionCache.get(fieldContextRegistry);
|
|
2530
2504
|
if (!contextCache) {
|
|
@@ -2891,7 +2865,7 @@ function applyCustomValidator(config, fieldPath) {
|
|
|
2891
2865
|
validatorFn = createFunctionValidator(config);
|
|
2892
2866
|
}
|
|
2893
2867
|
else {
|
|
2894
|
-
const logger = inject(
|
|
2868
|
+
const logger = inject(DynamicFormLogger);
|
|
2895
2869
|
logger.warn('Custom validator must have either "expression" or "functionName"');
|
|
2896
2870
|
return;
|
|
2897
2871
|
}
|
|
@@ -2903,7 +2877,7 @@ function applyCustomValidator(config, fieldPath) {
|
|
|
2903
2877
|
});
|
|
2904
2878
|
}
|
|
2905
2879
|
function createFunctionValidator(config) {
|
|
2906
|
-
const logger = inject(
|
|
2880
|
+
const logger = inject(DynamicFormLogger);
|
|
2907
2881
|
const functionName = config.functionName;
|
|
2908
2882
|
if (!functionName) {
|
|
2909
2883
|
logger.warn('Custom validator missing functionName');
|
|
@@ -2919,7 +2893,7 @@ function createFunctionValidator(config) {
|
|
|
2919
2893
|
return (ctx) => validatorFn(ctx, config.params);
|
|
2920
2894
|
}
|
|
2921
2895
|
function createExpressionValidator(config) {
|
|
2922
|
-
const logger = inject(
|
|
2896
|
+
const logger = inject(DynamicFormLogger);
|
|
2923
2897
|
const expression = config.expression;
|
|
2924
2898
|
if (!expression) {
|
|
2925
2899
|
logger.warn('Custom validator missing expression');
|
|
@@ -2930,7 +2904,7 @@ function createExpressionValidator(config) {
|
|
|
2930
2904
|
return (ctx) => {
|
|
2931
2905
|
try {
|
|
2932
2906
|
const evaluationContext = fieldContextRegistry.createEvaluationContext(ctx, functionRegistry.getCustomFunctions());
|
|
2933
|
-
const result = ExpressionParser.evaluate(
|
|
2907
|
+
const result = ExpressionParser.evaluate(expression, evaluationContext);
|
|
2934
2908
|
if (result) {
|
|
2935
2909
|
return null;
|
|
2936
2910
|
}
|
|
@@ -2966,7 +2940,7 @@ function createExpressionValidator(config) {
|
|
|
2966
2940
|
* - onError: Optional handler for resource errors
|
|
2967
2941
|
*/
|
|
2968
2942
|
function applyAsyncValidator(config, fieldPath) {
|
|
2969
|
-
const logger = inject(
|
|
2943
|
+
const logger = inject(DynamicFormLogger);
|
|
2970
2944
|
const registry = inject(FunctionRegistryService);
|
|
2971
2945
|
const validatorConfig = registry.getAsyncValidator(config.functionName);
|
|
2972
2946
|
if (!validatorConfig) {
|
|
@@ -2996,7 +2970,7 @@ function applyAsyncValidator(config, fieldPath) {
|
|
|
2996
2970
|
* - onError: Optional handler for HTTP errors
|
|
2997
2971
|
*/
|
|
2998
2972
|
function applyHttpValidator(config, fieldPath) {
|
|
2999
|
-
const logger = inject(
|
|
2973
|
+
const logger = inject(DynamicFormLogger);
|
|
3000
2974
|
const registry = inject(FunctionRegistryService);
|
|
3001
2975
|
const httpValidatorConfig = registry.getHttpValidator(config.functionName);
|
|
3002
2976
|
if (!httpValidatorConfig) {
|
|
@@ -3085,10 +3059,10 @@ class SchemaRegistryService {
|
|
|
3085
3059
|
}
|
|
3086
3060
|
return schema;
|
|
3087
3061
|
}
|
|
3088
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
3089
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.
|
|
3062
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SchemaRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
3063
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SchemaRegistryService });
|
|
3090
3064
|
}
|
|
3091
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
3065
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SchemaRegistryService, decorators: [{
|
|
3092
3066
|
type: Injectable
|
|
3093
3067
|
}] });
|
|
3094
3068
|
|
|
@@ -3135,7 +3109,7 @@ function createTypePredicateFunction(predicate, logger) {
|
|
|
3135
3109
|
* 3. The actual schema application happens at runtime via the schema function
|
|
3136
3110
|
*/
|
|
3137
3111
|
function applySchema(config, fieldPath) {
|
|
3138
|
-
const logger = inject(
|
|
3112
|
+
const logger = inject(DynamicFormLogger);
|
|
3139
3113
|
const schemaRegistry = inject(SchemaRegistryService);
|
|
3140
3114
|
const schema = schemaRegistry.resolveSchema(config.schema);
|
|
3141
3115
|
if (!schema) {
|
|
@@ -3196,6 +3170,7 @@ function createSchemaFunction(schema) {
|
|
|
3196
3170
|
/**
|
|
3197
3171
|
* Type guard for GroupField with proper type narrowing
|
|
3198
3172
|
*/
|
|
3173
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Type guard must accept any field type
|
|
3199
3174
|
function isGroupField(field) {
|
|
3200
3175
|
return field.type === 'group' && 'fields' in field;
|
|
3201
3176
|
}
|
|
@@ -3203,6 +3178,7 @@ function isGroupField(field) {
|
|
|
3203
3178
|
/**
|
|
3204
3179
|
* Type guard for ArrayField with proper type narrowing
|
|
3205
3180
|
*/
|
|
3181
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Type guard must accept any field type
|
|
3206
3182
|
function isArrayField(field) {
|
|
3207
3183
|
return field.type === 'array' && 'fields' in field;
|
|
3208
3184
|
}
|
|
@@ -3210,6 +3186,7 @@ function isArrayField(field) {
|
|
|
3210
3186
|
/**
|
|
3211
3187
|
* Type guard for RowField with proper type narrowing
|
|
3212
3188
|
*/
|
|
3189
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Type guard must accept any field type
|
|
3213
3190
|
function isRowField(field) {
|
|
3214
3191
|
return field.type === 'row' && 'fields' in field && Array.isArray(field.fields);
|
|
3215
3192
|
}
|
|
@@ -3610,74 +3587,9 @@ function isDisplayOnlyField(field) {
|
|
|
3610
3587
|
return field.type === 'text' || isContainerField(field);
|
|
3611
3588
|
}
|
|
3612
3589
|
|
|
3613
|
-
/**
|
|
3614
|
-
* Type guard to check if a value is a DynamicFormFeature
|
|
3615
|
-
*/
|
|
3616
|
-
function isDynamicFormFeature(value) {
|
|
3617
|
-
return (typeof value === 'object' &&
|
|
3618
|
-
value !== null &&
|
|
3619
|
-
'ɵkind' in value &&
|
|
3620
|
-
typeof value.ɵkind === 'string' &&
|
|
3621
|
-
'ɵproviders' in value &&
|
|
3622
|
-
Array.isArray(value.ɵproviders));
|
|
3623
|
-
}
|
|
3624
|
-
/**
|
|
3625
|
-
* Helper to create a feature with proper typing
|
|
3626
|
-
*/
|
|
3627
|
-
function createFeature(kind, providers) {
|
|
3628
|
-
return {
|
|
3629
|
-
ɵkind: kind,
|
|
3630
|
-
ɵproviders: providers,
|
|
3631
|
-
};
|
|
3632
|
-
}
|
|
3633
|
-
|
|
3634
|
-
/**
|
|
3635
|
-
* Configure logging for dynamic forms.
|
|
3636
|
-
*
|
|
3637
|
-
* @example
|
|
3638
|
-
* ```typescript
|
|
3639
|
-
* // Set log level
|
|
3640
|
-
* provideDynamicForm(
|
|
3641
|
-
* ...withMaterialFields(),
|
|
3642
|
-
* withLogger({ level: LogLevel.Debug })
|
|
3643
|
-
* )
|
|
3644
|
-
*
|
|
3645
|
-
* // Disable logging entirely
|
|
3646
|
-
* provideDynamicForm(
|
|
3647
|
-
* ...withMaterialFields(),
|
|
3648
|
-
* withLogger({ level: LogLevel.Off })
|
|
3649
|
-
* )
|
|
3650
|
-
*
|
|
3651
|
-
* // Custom logger implementation
|
|
3652
|
-
* provideDynamicForm(
|
|
3653
|
-
* ...withMaterialFields(),
|
|
3654
|
-
* withLogger({ logger: mySentryLogger })
|
|
3655
|
-
* )
|
|
3656
|
-
* ```
|
|
3657
|
-
*
|
|
3658
|
-
* @param options - Logger configuration options
|
|
3659
|
-
* @returns A DynamicFormFeature that configures logging
|
|
3660
|
-
*/
|
|
3661
|
-
function withLogger(options = {}) {
|
|
3662
|
-
let logger;
|
|
3663
|
-
if (options.logger) {
|
|
3664
|
-
// Use custom logger implementation
|
|
3665
|
-
logger = options.logger;
|
|
3666
|
-
}
|
|
3667
|
-
else if (options.level === LogLevel.Off) {
|
|
3668
|
-
// Explicit disable - use noop
|
|
3669
|
-
logger = new NoopLogger();
|
|
3670
|
-
}
|
|
3671
|
-
else {
|
|
3672
|
-
// Use console logger with specified level (default to Warn)
|
|
3673
|
-
logger = new ConsoleLogger(options.level ?? LogLevel.Warn);
|
|
3674
|
-
}
|
|
3675
|
-
return createFeature('logger', [{ provide: DYNAMIC_FORM_LOGGER, useValue: logger }]);
|
|
3676
|
-
}
|
|
3677
|
-
|
|
3678
3590
|
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
3679
3591
|
function getFieldTreeByKey(ctx, key) {
|
|
3680
|
-
return ctx.
|
|
3592
|
+
return ctx.fieldTree[key];
|
|
3681
3593
|
}
|
|
3682
3594
|
/**
|
|
3683
3595
|
* Creates an Angular signal forms schema from field definitions
|
|
@@ -3697,7 +3609,7 @@ function createSchemaFromFields(fields, registry, crossFieldValidators) {
|
|
|
3697
3609
|
// Inject services for cross-field validation
|
|
3698
3610
|
// These will be available because createSchemaFromFields is called within runInInjectionContext
|
|
3699
3611
|
const functionRegistry = inject(FunctionRegistryService);
|
|
3700
|
-
const logger = inject(
|
|
3612
|
+
const logger = inject(DynamicFormLogger);
|
|
3701
3613
|
return schema((path) => {
|
|
3702
3614
|
for (const fieldDef of fields) {
|
|
3703
3615
|
const valueHandling = getFieldValueHandling(fieldDef.type, registry);
|
|
@@ -3739,7 +3651,7 @@ function createSchemaFromFields(fields, registry, crossFieldValidators) {
|
|
|
3739
3651
|
* This is the key integration point that routes cross-field validation errors
|
|
3740
3652
|
* to the appropriate target fields via Angular's form state system.
|
|
3741
3653
|
*
|
|
3742
|
-
* The validateTree function allows returning errors with a `
|
|
3654
|
+
* The validateTree function allows returning errors with a `fieldTree` property
|
|
3743
3655
|
* that targets specific fields, which Angular automatically routes to those
|
|
3744
3656
|
* fields' errors() signal.
|
|
3745
3657
|
*
|
|
@@ -3765,7 +3677,7 @@ function applyCrossFieldTreeValidator(rootPath, validators, functionRegistry, lo
|
|
|
3765
3677
|
for (const entry of validators) {
|
|
3766
3678
|
const { sourceFieldKey, config } = entry;
|
|
3767
3679
|
try {
|
|
3768
|
-
const error = evaluateCrossFieldValidator(entry, formValue, sourceFieldKey, ctx, customFunctions);
|
|
3680
|
+
const error = evaluateCrossFieldValidator(entry, formValue, sourceFieldKey, ctx, customFunctions, logger);
|
|
3769
3681
|
if (error) {
|
|
3770
3682
|
errors.push(error);
|
|
3771
3683
|
}
|
|
@@ -3778,7 +3690,7 @@ function applyCrossFieldTreeValidator(rootPath, validators, functionRegistry, lo
|
|
|
3778
3690
|
const customConfig = config;
|
|
3779
3691
|
errors.push({
|
|
3780
3692
|
kind: customConfig.kind || config.type || 'custom',
|
|
3781
|
-
|
|
3693
|
+
fieldTree: targetField,
|
|
3782
3694
|
});
|
|
3783
3695
|
}
|
|
3784
3696
|
}
|
|
@@ -3789,7 +3701,7 @@ function applyCrossFieldTreeValidator(rootPath, validators, functionRegistry, lo
|
|
|
3789
3701
|
/**
|
|
3790
3702
|
* Evaluates a single cross-field validator entry and returns an error if validation fails.
|
|
3791
3703
|
*/
|
|
3792
|
-
function evaluateCrossFieldValidator(entry, formValue, sourceFieldKey, ctx, customFunctions) {
|
|
3704
|
+
function evaluateCrossFieldValidator(entry, formValue, sourceFieldKey, ctx, customFunctions, logger) {
|
|
3793
3705
|
const { config } = entry;
|
|
3794
3706
|
const fieldValue = formValue[sourceFieldKey];
|
|
3795
3707
|
// Create evaluation context for condition/expression evaluation
|
|
@@ -3798,6 +3710,7 @@ function evaluateCrossFieldValidator(entry, formValue, sourceFieldKey, ctx, cust
|
|
|
3798
3710
|
formValue,
|
|
3799
3711
|
fieldPath: sourceFieldKey,
|
|
3800
3712
|
customFunctions,
|
|
3713
|
+
logger,
|
|
3801
3714
|
};
|
|
3802
3715
|
// Check if this is a custom validator (with expression) or a built-in validator (with when condition)
|
|
3803
3716
|
if (config.type === 'custom') {
|
|
@@ -3815,7 +3728,7 @@ function evaluateCustomCrossFieldValidator(config, evaluationContext, sourceFiel
|
|
|
3815
3728
|
if (!config.expression) {
|
|
3816
3729
|
return null;
|
|
3817
3730
|
}
|
|
3818
|
-
const { fieldValue, formValue } = evaluationContext;
|
|
3731
|
+
const { fieldValue, formValue, logger, customFunctions } = evaluationContext;
|
|
3819
3732
|
// First, evaluate the when condition if present
|
|
3820
3733
|
// If the condition is false, the validator doesn't apply (validation passes)
|
|
3821
3734
|
if (config.when) {
|
|
@@ -3823,8 +3736,8 @@ function evaluateCustomCrossFieldValidator(config, evaluationContext, sourceFiel
|
|
|
3823
3736
|
fieldValue,
|
|
3824
3737
|
formValue,
|
|
3825
3738
|
fieldPath: sourceFieldKey,
|
|
3826
|
-
customFunctions:
|
|
3827
|
-
logger
|
|
3739
|
+
customFunctions: customFunctions || {},
|
|
3740
|
+
logger,
|
|
3828
3741
|
});
|
|
3829
3742
|
if (!conditionMet) {
|
|
3830
3743
|
return null; // Condition not met, skip validation
|
|
@@ -3843,7 +3756,7 @@ function evaluateCustomCrossFieldValidator(config, evaluationContext, sourceFiel
|
|
|
3843
3756
|
}
|
|
3844
3757
|
const errorObj = {
|
|
3845
3758
|
kind: config.kind || 'custom',
|
|
3846
|
-
|
|
3759
|
+
fieldTree: targetField,
|
|
3847
3760
|
};
|
|
3848
3761
|
// Evaluate and include errorParams for message interpolation
|
|
3849
3762
|
if (config.errorParams) {
|
|
@@ -3863,7 +3776,7 @@ function evaluateCustomCrossFieldValidator(config, evaluationContext, sourceFiel
|
|
|
3863
3776
|
* First checks the when condition, then applies the built-in validation logic.
|
|
3864
3777
|
*/
|
|
3865
3778
|
function evaluateBuiltInCrossFieldValidator(config, evaluationContext, sourceFieldKey, ctx) {
|
|
3866
|
-
const { fieldValue, formValue } = evaluationContext;
|
|
3779
|
+
const { fieldValue, formValue, logger, customFunctions } = evaluationContext;
|
|
3867
3780
|
// First, evaluate the when condition
|
|
3868
3781
|
// If the condition is false, the validator doesn't apply (validation passes)
|
|
3869
3782
|
if (config.when) {
|
|
@@ -3871,8 +3784,8 @@ function evaluateBuiltInCrossFieldValidator(config, evaluationContext, sourceFie
|
|
|
3871
3784
|
fieldValue,
|
|
3872
3785
|
formValue,
|
|
3873
3786
|
fieldPath: sourceFieldKey,
|
|
3874
|
-
customFunctions:
|
|
3875
|
-
logger
|
|
3787
|
+
customFunctions: customFunctions || {},
|
|
3788
|
+
logger,
|
|
3876
3789
|
});
|
|
3877
3790
|
if (!conditionMet) {
|
|
3878
3791
|
return null; // Condition not met, skip validation
|
|
@@ -3890,7 +3803,7 @@ function evaluateBuiltInCrossFieldValidator(config, evaluationContext, sourceFie
|
|
|
3890
3803
|
}
|
|
3891
3804
|
return {
|
|
3892
3805
|
kind: config.type,
|
|
3893
|
-
|
|
3806
|
+
fieldTree: targetField,
|
|
3894
3807
|
};
|
|
3895
3808
|
}
|
|
3896
3809
|
/**
|
|
@@ -4038,6 +3951,7 @@ class EventBus {
|
|
|
4038
3951
|
* eventBus.dispatch(CustomFormEvent);
|
|
4039
3952
|
* ```
|
|
4040
3953
|
*/
|
|
3954
|
+
// TypeScript limitation: Must use ConstructorParameters which relies on `any` in FormEventConstructor
|
|
4041
3955
|
dispatch(eventConstructor, ...args) {
|
|
4042
3956
|
this.pipeline$.next(new eventConstructor(...args));
|
|
4043
3957
|
}
|
|
@@ -4079,10 +3993,10 @@ class EventBus {
|
|
|
4079
3993
|
}
|
|
4080
3994
|
return this.pipeline$.pipe(filter((event) => event.type === eventType));
|
|
4081
3995
|
}
|
|
4082
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
4083
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.
|
|
3996
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: EventBus, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
3997
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: EventBus });
|
|
4084
3998
|
}
|
|
4085
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
3999
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: EventBus, decorators: [{
|
|
4086
4000
|
type: Injectable
|
|
4087
4001
|
}] });
|
|
4088
4002
|
|
|
@@ -4126,7 +4040,7 @@ class ComponentInitializedEvent {
|
|
|
4126
4040
|
* ```
|
|
4127
4041
|
*/
|
|
4128
4042
|
function createInitializationTracker(eventBus, expectedCount) {
|
|
4129
|
-
return eventBus.on('component-initialized').pipe(scan$1((count
|
|
4043
|
+
return eventBus.on('component-initialized').pipe(scan$1((count) => count + 1, 0), map$1((currentCount) => currentCount >= expectedCount), filter$1((isComplete) => isComplete));
|
|
4130
4044
|
}
|
|
4131
4045
|
/**
|
|
4132
4046
|
* Creates an observable that tracks component initialization progress with detailed status.
|
|
@@ -4151,7 +4065,7 @@ function createInitializationTracker(eventBus, expectedCount) {
|
|
|
4151
4065
|
* ```
|
|
4152
4066
|
*/
|
|
4153
4067
|
function createDetailedInitializationTracker(eventBus, expectedCount) {
|
|
4154
|
-
return eventBus.on('component-initialized').pipe(scan$1((count
|
|
4068
|
+
return eventBus.on('component-initialized').pipe(scan$1((count) => count + 1, 0), map$1((currentCount) => ({
|
|
4155
4069
|
currentCount,
|
|
4156
4070
|
expectedCount,
|
|
4157
4071
|
isComplete: currentCount >= expectedCount,
|
|
@@ -4584,6 +4498,9 @@ function tryCreateSchemaEntry(fieldKey, config) {
|
|
|
4584
4498
|
/** Converts a built-in validator with cross-field expression to a custom validator. */
|
|
4585
4499
|
function convertBuiltInToCustomValidator(config) {
|
|
4586
4500
|
const expression = config.expression;
|
|
4501
|
+
if (!expression) {
|
|
4502
|
+
throw new Error(`Built-in validator ${config.type} missing required expression for cross-field conversion`);
|
|
4503
|
+
}
|
|
4587
4504
|
let validationExpression;
|
|
4588
4505
|
switch (config.type) {
|
|
4589
4506
|
case 'min':
|
|
@@ -4659,11 +4576,8 @@ class FormModeValidator {
|
|
|
4659
4576
|
static validateFormConfigurationOrThrow(fields) {
|
|
4660
4577
|
const result = this.validateFormConfiguration(fields);
|
|
4661
4578
|
if (!result.isValid) {
|
|
4662
|
-
const errorMessage = [
|
|
4663
|
-
|
|
4664
|
-
...result.errors.map((error) => ` - ${error}`),
|
|
4665
|
-
].join('\n');
|
|
4666
|
-
throw new Error(errorMessage);
|
|
4579
|
+
const errorMessage = [`Invalid form configuration (${result.mode} mode):`, ...result.errors.map((error) => ` - ${error}`)].join('\n');
|
|
4580
|
+
throw new DynamicFormError(errorMessage);
|
|
4667
4581
|
}
|
|
4668
4582
|
}
|
|
4669
4583
|
/**
|
|
@@ -4885,7 +4799,7 @@ class PageOrchestratorComponent {
|
|
|
4885
4799
|
eventBus = inject(EventBus);
|
|
4886
4800
|
fieldContextRegistry = inject(FieldContextRegistryService);
|
|
4887
4801
|
functionRegistry = inject(FunctionRegistryService);
|
|
4888
|
-
logger = inject(
|
|
4802
|
+
logger = inject(DynamicFormLogger);
|
|
4889
4803
|
/**
|
|
4890
4804
|
* Array of page field definitions to render
|
|
4891
4805
|
*/
|
|
@@ -4898,6 +4812,7 @@ class PageOrchestratorComponent {
|
|
|
4898
4812
|
/**
|
|
4899
4813
|
* Field signal context for child fields
|
|
4900
4814
|
*/
|
|
4815
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- FieldSignalContext is contravariant in TModel, using any allows accepting any form type
|
|
4901
4816
|
fieldSignalContext = input.required(...(ngDevMode ? [{ debugName: "fieldSignalContext" }] : []));
|
|
4902
4817
|
/**
|
|
4903
4818
|
* Computed signal that tracks which pages are hidden.
|
|
@@ -4978,7 +4893,7 @@ class PageOrchestratorComponent {
|
|
|
4978
4893
|
const fieldKey = fieldDef.key;
|
|
4979
4894
|
if (!fieldKey)
|
|
4980
4895
|
continue;
|
|
4981
|
-
// Access the field from the form using
|
|
4896
|
+
// Access the field from the form using bracket notation
|
|
4982
4897
|
const field = form[fieldKey];
|
|
4983
4898
|
if (field && typeof field === 'function') {
|
|
4984
4899
|
const fieldState = field();
|
|
@@ -5143,7 +5058,7 @@ class PageOrchestratorComponent {
|
|
|
5143
5058
|
}
|
|
5144
5059
|
// Check each hidden logic - if ANY condition is true, the page is hidden
|
|
5145
5060
|
for (const logic of hiddenLogic) {
|
|
5146
|
-
// Handle static boolean conditions
|
|
5061
|
+
// Handle static boolean conditions (fast path)
|
|
5147
5062
|
if (typeof logic.condition === 'boolean') {
|
|
5148
5063
|
if (logic.condition) {
|
|
5149
5064
|
return true;
|
|
@@ -5159,8 +5074,8 @@ class PageOrchestratorComponent {
|
|
|
5159
5074
|
}
|
|
5160
5075
|
return false;
|
|
5161
5076
|
}
|
|
5162
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
5163
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.
|
|
5077
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PageOrchestratorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5078
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: PageOrchestratorComponent, isStandalone: true, selector: "div[page-orchestrator]", inputs: { pageFields: { classPropertyName: "pageFields", publicName: "pageFields", isSignal: true, isRequired: true, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: true, transformFunction: null }, fieldSignalContext: { classPropertyName: "fieldSignalContext", publicName: "fieldSignalContext", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.data-current-page": "state().currentPageIndex", "attr.data-total-pages": "state().totalPages" }, classAttribute: "df-page-orchestrator" }, providers: [
|
|
5164
5079
|
{
|
|
5165
5080
|
provide: FIELD_SIGNAL_CONTEXT,
|
|
5166
5081
|
useFactory: (orchestrator) => orchestrator.extendedFieldSignalContext(),
|
|
@@ -5190,9 +5105,9 @@ class PageOrchestratorComponent {
|
|
|
5190
5105
|
}
|
|
5191
5106
|
}
|
|
5192
5107
|
}
|
|
5193
|
-
`, isInline: true, styles: [":host{display:block;position:relative;width:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [import('./ng-forge-dynamic-forms-page-field.component-
|
|
5108
|
+
`, isInline: true, styles: [":host{display:block;position:relative;width:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, deferBlockDependencies: [() => [import('./ng-forge-dynamic-forms-page-field.component-BM32StxL.mjs').then(m => m.default)], () => [import('./ng-forge-dynamic-forms-page-field.component-BM32StxL.mjs').then(m => m.default)]] });
|
|
5194
5109
|
}
|
|
5195
|
-
i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "21.0.
|
|
5110
|
+
i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "21.0.6", ngImport: i0, type: PageOrchestratorComponent, resolveDeferredDeps: () => [import('./ng-forge-dynamic-forms-page-field.component-BM32StxL.mjs').then(m => m.default)], resolveMetadata: PageFieldComponent => ({ decorators: [{
|
|
5196
5111
|
type: Component,
|
|
5197
5112
|
args: [{ selector: 'div[page-orchestrator]', imports: [PageFieldComponent], template: `
|
|
5198
5113
|
@for (pageField of pageFields(); track pageField.key; let i = $index) {
|
|
@@ -5261,7 +5176,7 @@ class DynamicForm {
|
|
|
5261
5176
|
rootFormRegistry = inject(RootFormRegistryService);
|
|
5262
5177
|
functionRegistry = inject(FunctionRegistryService);
|
|
5263
5178
|
schemaRegistry = inject(SchemaRegistryService);
|
|
5264
|
-
logger = inject(
|
|
5179
|
+
logger = inject(DynamicFormLogger);
|
|
5265
5180
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
5266
5181
|
// Inputs
|
|
5267
5182
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -5629,13 +5544,13 @@ class DynamicForm {
|
|
|
5629
5544
|
}
|
|
5630
5545
|
onFormReset() {
|
|
5631
5546
|
const defaults = this.defaultValues();
|
|
5632
|
-
//
|
|
5547
|
+
// Type assertion is necessary: Angular Signal Forms expects exact form value type
|
|
5633
5548
|
this.form()().value.set(defaults);
|
|
5634
5549
|
this.value.set(defaults);
|
|
5635
5550
|
}
|
|
5636
5551
|
onFormClear() {
|
|
5637
5552
|
const emptyValue = {};
|
|
5638
|
-
//
|
|
5553
|
+
// Type assertion is necessary: Angular Signal Forms expects exact form value type
|
|
5639
5554
|
this.form()().value.set(emptyValue);
|
|
5640
5555
|
this.value.set(emptyValue);
|
|
5641
5556
|
}
|
|
@@ -5645,8 +5560,8 @@ class DynamicForm {
|
|
|
5645
5560
|
ngOnDestroy() {
|
|
5646
5561
|
this.rootFormRegistry.unregisterForm();
|
|
5647
5562
|
}
|
|
5648
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
5649
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.
|
|
5563
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DynamicForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5564
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: DynamicForm, isStandalone: true, selector: "form[dynamic-form]", inputs: { config: { classPropertyName: "config", publicName: "dynamic-form", isSignal: true, isRequired: true, transformFunction: null }, formOptions: { classPropertyName: "formOptions", publicName: "formOptions", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", validityChange: "validityChange", dirtyChange: "dirtyChange", submitted: "submitted", reset: "reset", cleared: "cleared", events: "events", initialized: "initialized", onPageChange: "onPageChange", onPageNavigationStateChange: "onPageNavigationStateChange" }, host: { listeners: { "submit": "onNativeSubmit($event)" }, properties: { "class.disabled": "disabled()", "class.df-form-paged": "formModeDetection().mode === \"paged\"", "class.df-form-non-paged": "formModeDetection().mode === \"non-paged\"", "attr.data-form-mode": "formModeDetection().mode" }, classAttribute: "df-dynamic-form df-form" }, providers: [EventBus, SchemaRegistryService, FunctionRegistryService, RootFormRegistryService, FieldContextRegistryService], ngImport: i0, template: `
|
|
5650
5565
|
@if (formModeDetection().mode === 'paged') {
|
|
5651
5566
|
<div page-orchestrator [pageFields]="pageFieldDefinitions()" [form]="form()" [fieldSignalContext]="fieldSignalContext()"></div>
|
|
5652
5567
|
} @else {
|
|
@@ -5656,7 +5571,7 @@ class DynamicForm {
|
|
|
5656
5571
|
}
|
|
5657
5572
|
`, isInline: true, styles: [":host,.df-form{--df-grid-columns: 12;--df-grid-gap: .5rem;--df-grid-row-gap: .5rem;--df-breakpoint-sm: 576px;--df-breakpoint-md: 768px;--df-breakpoint-lg: 992px;--df-breakpoint-xl: 1200px;--df-grid-gap-sm: .5rem;--df-grid-gap-md: .5rem;--df-grid-gap-lg: .5rem;--df-grid-gap-xl: .5rem;--df-grid-row-gap-sm: .5rem;--df-grid-row-gap-md: .5rem;--df-grid-row-gap-lg: .5rem;--df-grid-row-gap-xl: .5rem;--df-group-gap: var(--df-grid-gap);--df-group-padding: var(--df-grid-gap)}.df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap);width:100%}.df-form>*{grid-column:1/-1}.df-row{display:grid;grid-template-columns:repeat(var(--df-grid-columns, 12),1fr);gap:var(--df-grid-gap);align-items:start;width:100%}.df-row>*:not([class*=df-col-]){grid-column:1/-1}.df-col-1{grid-column:span 1}.df-col-2{grid-column:span 2}.df-col-3{grid-column:span 3}.df-col-4{grid-column:span 4}.df-col-5{grid-column:span 5}.df-col-6{grid-column:span 6}.df-col-7{grid-column:span 7}.df-col-8{grid-column:span 8}.df-col-9{grid-column:span 9}.df-col-10{grid-column:span 10}.df-col-11{grid-column:span 11}.df-col-12{grid-column:span 12}.df-col-auto{grid-column:span auto;width:auto}.df-col-full{grid-column:1/-1}.df-col-start-1{grid-column-start:1}.df-col-start-2{grid-column-start:2}.df-col-start-3{grid-column-start:3}.df-col-start-4{grid-column-start:4}.df-col-start-5{grid-column-start:5}.df-col-start-6{grid-column-start:6}.df-col-start-7{grid-column-start:7}.df-col-start-8{grid-column-start:8}.df-col-start-9{grid-column-start:9}.df-col-start-10{grid-column-start:10}.df-col-start-11{grid-column-start:11}.df-col-start-12{grid-column-start:12}.df-col-end-1{grid-column-end:1}.df-col-end-2{grid-column-end:2}.df-col-end-3{grid-column-end:3}.df-col-end-4{grid-column-end:4}.df-col-end-5{grid-column-end:5}.df-col-end-6{grid-column-end:6}.df-col-end-7{grid-column-end:7}.df-col-end-8{grid-column-end:8}.df-col-end-9{grid-column-end:9}.df-col-end-10{grid-column-end:10}.df-col-end-11{grid-column-end:11}.df-col-end-12{grid-column-end:12}.df-col-end-13{grid-column-end:13}@media(max-width:576px){.df-form{--df-grid-gap: var(--df-grid-gap-sm);--df-grid-row-gap: var(--df-grid-row-gap-sm)}.df-row{grid-template-columns:1fr}.df-row>*{grid-column:1/-1!important}.df-row.df-row-mobile-keep-cols{grid-template-columns:repeat(var(--df-grid-columns),1fr)}.df-row.df-row-mobile-keep-cols>*{grid-column:revert!important}}@media(min-width:577px)and (max-width:768px){.df-form{--df-grid-gap: var(--df-grid-gap-md);--df-grid-row-gap: var(--df-grid-row-gap-md)}.df-row{--df-grid-columns: 6}.df-col-sm-1{grid-column:span 1}.df-col-sm-2{grid-column:span 2}.df-col-sm-3{grid-column:span 3}.df-col-sm-4{grid-column:span 4}.df-col-sm-5{grid-column:span 5}.df-col-sm-6{grid-column:span 6}.df-col-sm-full{grid-column:1/-1}}@media(min-width:769px)and (max-width:992px){.df-form{--df-grid-gap: var(--df-grid-gap-lg);--df-grid-row-gap: var(--df-grid-row-gap-lg)}.df-col-md-1{grid-column:span 1}.df-col-md-2{grid-column:span 2}.df-col-md-3{grid-column:span 3}.df-col-md-4{grid-column:span 4}.df-col-md-5{grid-column:span 5}.df-col-md-6{grid-column:span 6}.df-col-md-7{grid-column:span 7}.df-col-md-8{grid-column:span 8}.df-col-md-9{grid-column:span 9}.df-col-md-10{grid-column:span 10}.df-col-md-11{grid-column:span 11}.df-col-md-12{grid-column:span 12}.df-col-md-full{grid-column:1/-1}}@media(min-width:993px){.df-form{--df-grid-gap: var(--df-grid-gap-xl);--df-grid-row-gap: var(--df-grid-row-gap-xl)}.df-col-lg-1{grid-column:span 1}.df-col-lg-2{grid-column:span 2}.df-col-lg-3{grid-column:span 3}.df-col-lg-4{grid-column:span 4}.df-col-lg-5{grid-column:span 5}.df-col-lg-6{grid-column:span 6}.df-col-lg-7{grid-column:span 7}.df-col-lg-8{grid-column:span 8}.df-col-lg-9{grid-column:span 9}.df-col-lg-10{grid-column:span 10}.df-col-lg-11{grid-column:span 11}.df-col-lg-12{grid-column:span 12}.df-col-lg-full{grid-column:1/-1}}.df-gap-none{--df-grid-gap: 0}.df-gap-xs{--df-grid-gap: .25rem}.df-gap-sm{--df-grid-gap: .5rem}.df-gap-md{--df-grid-gap: 1rem}.df-gap-lg{--df-grid-gap: 1.5rem}.df-gap-xl{--df-grid-gap: 2rem}.df-row-gap-none{--df-grid-row-gap: 0}.df-row-gap-xs{--df-grid-row-gap: .25rem}.df-row-gap-sm{--df-grid-row-gap: .5rem}.df-row-gap-md{--df-grid-row-gap: 1rem}.df-row-gap-lg{--df-grid-row-gap: 1.5rem}.df-row-gap-xl{--df-grid-row-gap: 2rem}.df-field{display:block;width:100%;min-width:0;overflow:hidden;margin:0}.df-group,.df-page{display:block;width:100%}.df-form.disabled,.df-row.disabled,.df-field.disabled{opacity:.6;pointer-events:none}.df-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-sr-only-focusable:focus,.df-sr-only-focusable:active{position:static;width:auto;height:auto;padding:inherit;margin:inherit;overflow:visible;clip:auto;white-space:normal}.df-live-region{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-form :focus-visible{outline:2px solid var(--df-focus-color, #005fcc);outline-offset:2px}.df-group:focus-within{outline:1px dashed var(--df-focus-color, #005fcc);outline-offset:4px}:host{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap);width:100%}:host>*{grid-column:1/-1}:host.df-form-spacing-custom{--df-grid-row-gap: var(--form-custom-spacing, 1.5rem)}:host.df-form-gap-none{--df-grid-row-gap: 0}:host.df-form-gap-xs{--df-grid-row-gap: .25rem}:host.df-form-gap-sm{--df-grid-row-gap: .5rem}:host.df-form-gap-md{--df-grid-row-gap: 1rem}:host.df-form-gap-lg{--df-grid-row-gap: 1.5rem}:host.df-form-gap-xl{--df-grid-row-gap: 2rem}:host.df-form-paged{--df-grid-row-gap: 0}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: PageOrchestratorComponent, selector: "div[page-orchestrator]", inputs: ["pageFields", "form", "fieldSignalContext"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
5658
5573
|
}
|
|
5659
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
5574
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DynamicForm, decorators: [{
|
|
5660
5575
|
type: Component,
|
|
5661
5576
|
args: [{ selector: 'form[dynamic-form]', imports: [NgComponentOutlet, PageOrchestratorComponent], template: `
|
|
5662
5577
|
@if (formModeDetection().mode === 'paged') {
|
|
@@ -5759,7 +5674,6 @@ function pageFieldMapper(fieldDef) {
|
|
|
5759
5674
|
function textFieldMapper(fieldDef) {
|
|
5760
5675
|
const fieldContextRegistry = inject(FieldContextRegistryService);
|
|
5761
5676
|
const functionRegistry = inject(FunctionRegistryService);
|
|
5762
|
-
const logger = inject(DYNAMIC_FORM_LOGGER);
|
|
5763
5677
|
// Build base inputs (static, from field definition)
|
|
5764
5678
|
const baseInputs = buildBaseInputs(fieldDef);
|
|
5765
5679
|
// Create computed signal for hidden state based on logic configuration
|
|
@@ -5814,6 +5728,13 @@ function textFieldMapper(fieldDef) {
|
|
|
5814
5728
|
* { type: 'page', key: 'step1', fields: [...] }
|
|
5815
5729
|
* ```
|
|
5816
5730
|
*/
|
|
5731
|
+
/**
|
|
5732
|
+
* Built-in field types provided by the dynamic form library.
|
|
5733
|
+
*
|
|
5734
|
+
* Each field type is validated at compile time using satisfies, ensuring
|
|
5735
|
+
* type safety of the mapper function while allowing the array to be typed
|
|
5736
|
+
* as FieldTypeDefinition[] for consumer flexibility.
|
|
5737
|
+
*/
|
|
5817
5738
|
const BUILT_IN_FIELDS = [
|
|
5818
5739
|
{
|
|
5819
5740
|
name: 'row',
|
|
@@ -5835,25 +5756,65 @@ const BUILT_IN_FIELDS = [
|
|
|
5835
5756
|
},
|
|
5836
5757
|
{
|
|
5837
5758
|
name: 'page',
|
|
5838
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-page-field.component-
|
|
5759
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-page-field.component-BM32StxL.mjs'),
|
|
5839
5760
|
mapper: pageFieldMapper,
|
|
5840
5761
|
valueHandling: 'flatten',
|
|
5841
5762
|
},
|
|
5842
5763
|
{
|
|
5843
5764
|
name: 'text',
|
|
5844
|
-
loadComponent: () => import('./ng-forge-dynamic-forms-text-field.component-
|
|
5765
|
+
loadComponent: () => import('./ng-forge-dynamic-forms-text-field.component-C0I8jf9T.mjs'),
|
|
5845
5766
|
mapper: textFieldMapper,
|
|
5846
5767
|
valueHandling: 'exclude',
|
|
5847
5768
|
},
|
|
5848
5769
|
];
|
|
5849
5770
|
|
|
5771
|
+
/**
|
|
5772
|
+
* Type guard to check if a value is a DynamicFormFeature
|
|
5773
|
+
*/
|
|
5774
|
+
function isDynamicFormFeature(value) {
|
|
5775
|
+
return (typeof value === 'object' &&
|
|
5776
|
+
value !== null &&
|
|
5777
|
+
'ɵkind' in value &&
|
|
5778
|
+
typeof value.ɵkind === 'string' &&
|
|
5779
|
+
'ɵproviders' in value &&
|
|
5780
|
+
Array.isArray(value.ɵproviders));
|
|
5781
|
+
}
|
|
5782
|
+
/**
|
|
5783
|
+
* Helper to create a feature with proper typing
|
|
5784
|
+
*/
|
|
5785
|
+
function createFeature(kind, providers) {
|
|
5786
|
+
return {
|
|
5787
|
+
ɵkind: kind,
|
|
5788
|
+
ɵproviders: providers,
|
|
5789
|
+
};
|
|
5790
|
+
}
|
|
5791
|
+
|
|
5792
|
+
/**
|
|
5793
|
+
* Console-based logger implementation.
|
|
5794
|
+
*/
|
|
5795
|
+
class ConsoleLogger {
|
|
5796
|
+
prefix = '[Dynamic Forms]';
|
|
5797
|
+
debug(message, ...args) {
|
|
5798
|
+
console.debug(this.prefix, message, ...args);
|
|
5799
|
+
}
|
|
5800
|
+
info(message, ...args) {
|
|
5801
|
+
console.info(this.prefix, message, ...args);
|
|
5802
|
+
}
|
|
5803
|
+
warn(message, ...args) {
|
|
5804
|
+
console.warn(this.prefix, message, ...args);
|
|
5805
|
+
}
|
|
5806
|
+
error(message, ...args) {
|
|
5807
|
+
console.error(this.prefix, message, ...args);
|
|
5808
|
+
}
|
|
5809
|
+
}
|
|
5810
|
+
|
|
5850
5811
|
/**
|
|
5851
5812
|
* Provider function to configure the dynamic form system with field types and options.
|
|
5852
5813
|
*
|
|
5853
5814
|
* This function creates environment providers that can be used at application or route level
|
|
5854
5815
|
* to register field types. It provides type-safe field registration with automatic type inference.
|
|
5855
5816
|
*
|
|
5856
|
-
* @param items - Field type definitions and/or features (like
|
|
5817
|
+
* @param items - Field type definitions and/or features (like withLoggerConfig)
|
|
5857
5818
|
* @returns Environment providers for dependency injection with type inference
|
|
5858
5819
|
*
|
|
5859
5820
|
* @example
|
|
@@ -5868,12 +5829,12 @@ const BUILT_IN_FIELDS = [
|
|
|
5868
5829
|
*
|
|
5869
5830
|
* @example
|
|
5870
5831
|
* ```typescript
|
|
5871
|
-
* //
|
|
5832
|
+
* // Disable logging
|
|
5872
5833
|
* bootstrapApplication(AppComponent, {
|
|
5873
5834
|
* providers: [
|
|
5874
5835
|
* provideDynamicForm(
|
|
5875
5836
|
* ...withMaterialFields(),
|
|
5876
|
-
*
|
|
5837
|
+
* withLoggerConfig(false)
|
|
5877
5838
|
* )
|
|
5878
5839
|
* ]
|
|
5879
5840
|
* });
|
|
@@ -5881,18 +5842,6 @@ const BUILT_IN_FIELDS = [
|
|
|
5881
5842
|
*
|
|
5882
5843
|
* @example
|
|
5883
5844
|
* ```typescript
|
|
5884
|
-
* // Route-level setup
|
|
5885
|
-
* const routes: Routes = [
|
|
5886
|
-
* {
|
|
5887
|
-
* path: 'form',
|
|
5888
|
-
* component: FormComponent,
|
|
5889
|
-
* providers: [provideDynamicForm(...withMaterialFields())]
|
|
5890
|
-
* }
|
|
5891
|
-
* ];
|
|
5892
|
-
* ```
|
|
5893
|
-
*
|
|
5894
|
-
* @example
|
|
5895
|
-
* ```typescript
|
|
5896
5845
|
* // Custom field types with type inference
|
|
5897
5846
|
* import { CustomFieldType, AnotherFieldType } from './custom-fields';
|
|
5898
5847
|
*
|
|
@@ -5922,14 +5871,18 @@ function provideDynamicForm(...items) {
|
|
|
5922
5871
|
});
|
|
5923
5872
|
// Extract providers from features
|
|
5924
5873
|
const featureProviders = [];
|
|
5874
|
+
const hasLoggerFeature = features.some((feature) => feature.ɵkind === 'logger');
|
|
5925
5875
|
features.forEach((feature) => {
|
|
5926
5876
|
featureProviders.push(...feature.ɵproviders);
|
|
5927
5877
|
});
|
|
5878
|
+
// Default logger provider (ConsoleLogger) if no logger feature was provided
|
|
5879
|
+
const defaultLoggerProvider = hasLoggerFeature ? [] : [{ provide: DynamicFormLogger, useValue: new ConsoleLogger() }];
|
|
5928
5880
|
return makeEnvironmentProviders([
|
|
5881
|
+
...defaultLoggerProvider,
|
|
5929
5882
|
{
|
|
5930
5883
|
provide: FIELD_REGISTRY,
|
|
5931
5884
|
useFactory: () => {
|
|
5932
|
-
const logger = inject(
|
|
5885
|
+
const logger = inject(DynamicFormLogger);
|
|
5933
5886
|
const registry = new Map();
|
|
5934
5887
|
// Add custom field types
|
|
5935
5888
|
fields.forEach((fieldType) => {
|
|
@@ -5946,6 +5899,55 @@ function provideDynamicForm(...items) {
|
|
|
5946
5899
|
]);
|
|
5947
5900
|
}
|
|
5948
5901
|
|
|
5902
|
+
/**
|
|
5903
|
+
* No-operation logger implementation.
|
|
5904
|
+
* All methods are no-ops - used in production by default.
|
|
5905
|
+
*/
|
|
5906
|
+
class NoopLogger {
|
|
5907
|
+
debug() {
|
|
5908
|
+
// Intentionally empty
|
|
5909
|
+
}
|
|
5910
|
+
info() {
|
|
5911
|
+
// Intentionally empty
|
|
5912
|
+
}
|
|
5913
|
+
warn() {
|
|
5914
|
+
// Intentionally empty
|
|
5915
|
+
}
|
|
5916
|
+
error() {
|
|
5917
|
+
// Intentionally empty
|
|
5918
|
+
}
|
|
5919
|
+
}
|
|
5920
|
+
|
|
5921
|
+
/**
|
|
5922
|
+
* Configure logging for dynamic forms.
|
|
5923
|
+
*
|
|
5924
|
+
* By default, logging is enabled (ConsoleLogger). Use this feature to disable logging
|
|
5925
|
+
* or configure it conditionally.
|
|
5926
|
+
*
|
|
5927
|
+
* @example
|
|
5928
|
+
* ```typescript
|
|
5929
|
+
* // Disable logging
|
|
5930
|
+
* provideDynamicForm(
|
|
5931
|
+
* ...withMaterialFields(),
|
|
5932
|
+
* withLoggerConfig(false)
|
|
5933
|
+
* )
|
|
5934
|
+
*
|
|
5935
|
+
* // Conditional logging (e.g., only in dev mode)
|
|
5936
|
+
* provideDynamicForm(
|
|
5937
|
+
* ...withMaterialFields(),
|
|
5938
|
+
* withLoggerConfig(() => isDevMode())
|
|
5939
|
+
* )
|
|
5940
|
+
* ```
|
|
5941
|
+
*
|
|
5942
|
+
* @param enabled - Whether logging is enabled. Can be a boolean or a function that returns a boolean. Defaults to true.
|
|
5943
|
+
* @returns A DynamicFormFeature that configures logging
|
|
5944
|
+
*/
|
|
5945
|
+
function withLoggerConfig(enabled = true) {
|
|
5946
|
+
const isEnabled = typeof enabled === 'function' ? enabled() : enabled;
|
|
5947
|
+
const logger = isEnabled ? new ConsoleLogger() : new NoopLogger();
|
|
5948
|
+
return createFeature('logger', [{ provide: DynamicFormLogger, useValue: logger }]);
|
|
5949
|
+
}
|
|
5950
|
+
|
|
5949
5951
|
/**
|
|
5950
5952
|
* No-op logger for when no logger is provided.
|
|
5951
5953
|
* Used as fallback in button logic evaluation to avoid breaking downstream packages.
|
|
@@ -6262,10 +6264,10 @@ class DynamicTextPipe {
|
|
|
6262
6264
|
}
|
|
6263
6265
|
return of(value || '');
|
|
6264
6266
|
}
|
|
6265
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
6266
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.
|
|
6267
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DynamicTextPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
6268
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.6", ngImport: i0, type: DynamicTextPipe, isStandalone: true, name: "dynamicText" });
|
|
6267
6269
|
}
|
|
6268
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
6270
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: DynamicTextPipe, decorators: [{
|
|
6269
6271
|
type: Pipe,
|
|
6270
6272
|
args: [{
|
|
6271
6273
|
name: 'dynamicText',
|
|
@@ -6311,7 +6313,7 @@ class RowFieldComponent {
|
|
|
6311
6313
|
fieldRegistry = injectFieldRegistry();
|
|
6312
6314
|
injector = inject(Injector);
|
|
6313
6315
|
eventBus = inject(EventBus);
|
|
6314
|
-
logger = inject(
|
|
6316
|
+
logger = inject(DynamicFormLogger);
|
|
6315
6317
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
6316
6318
|
// Inputs
|
|
6317
6319
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -6368,14 +6370,14 @@ class RowFieldComponent {
|
|
|
6368
6370
|
}
|
|
6369
6371
|
});
|
|
6370
6372
|
}
|
|
6371
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
6372
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.
|
|
6373
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: RowFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6374
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: RowFieldComponent, isStandalone: true, selector: "div[row-field]", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "class.disabled": "disabled()", "id": "`${key()}`", "attr.data-testid": "key()" }, classAttribute: "df-field df-row" }, ngImport: i0, template: `
|
|
6373
6375
|
@for (field of resolvedFields(); track field.key) {
|
|
6374
6376
|
<ng-container *ngComponentOutlet="field.component; injector: field.injector; inputs: field.inputs()" />
|
|
6375
6377
|
}
|
|
6376
6378
|
`, isInline: true, styles: [":host,.df-form{--df-grid-columns: 12;--df-grid-gap: .5rem;--df-grid-row-gap: .5rem;--df-breakpoint-sm: 576px;--df-breakpoint-md: 768px;--df-breakpoint-lg: 992px;--df-breakpoint-xl: 1200px;--df-grid-gap-sm: .5rem;--df-grid-gap-md: .5rem;--df-grid-gap-lg: .5rem;--df-grid-gap-xl: .5rem;--df-grid-row-gap-sm: .5rem;--df-grid-row-gap-md: .5rem;--df-grid-row-gap-lg: .5rem;--df-grid-row-gap-xl: .5rem;--df-group-gap: var(--df-grid-gap);--df-group-padding: var(--df-grid-gap)}.df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap);width:100%}.df-form>*{grid-column:1/-1}.df-row{display:grid;grid-template-columns:repeat(var(--df-grid-columns, 12),1fr);gap:var(--df-grid-gap);align-items:start;width:100%}.df-row>*:not([class*=df-col-]){grid-column:1/-1}.df-col-1{grid-column:span 1}.df-col-2{grid-column:span 2}.df-col-3{grid-column:span 3}.df-col-4{grid-column:span 4}.df-col-5{grid-column:span 5}.df-col-6{grid-column:span 6}.df-col-7{grid-column:span 7}.df-col-8{grid-column:span 8}.df-col-9{grid-column:span 9}.df-col-10{grid-column:span 10}.df-col-11{grid-column:span 11}.df-col-12{grid-column:span 12}.df-col-auto{grid-column:span auto;width:auto}.df-col-full{grid-column:1/-1}.df-col-start-1{grid-column-start:1}.df-col-start-2{grid-column-start:2}.df-col-start-3{grid-column-start:3}.df-col-start-4{grid-column-start:4}.df-col-start-5{grid-column-start:5}.df-col-start-6{grid-column-start:6}.df-col-start-7{grid-column-start:7}.df-col-start-8{grid-column-start:8}.df-col-start-9{grid-column-start:9}.df-col-start-10{grid-column-start:10}.df-col-start-11{grid-column-start:11}.df-col-start-12{grid-column-start:12}.df-col-end-1{grid-column-end:1}.df-col-end-2{grid-column-end:2}.df-col-end-3{grid-column-end:3}.df-col-end-4{grid-column-end:4}.df-col-end-5{grid-column-end:5}.df-col-end-6{grid-column-end:6}.df-col-end-7{grid-column-end:7}.df-col-end-8{grid-column-end:8}.df-col-end-9{grid-column-end:9}.df-col-end-10{grid-column-end:10}.df-col-end-11{grid-column-end:11}.df-col-end-12{grid-column-end:12}.df-col-end-13{grid-column-end:13}@media(max-width:576px){.df-form{--df-grid-gap: var(--df-grid-gap-sm);--df-grid-row-gap: var(--df-grid-row-gap-sm)}.df-row{grid-template-columns:1fr}.df-row>*{grid-column:1/-1!important}.df-row.df-row-mobile-keep-cols{grid-template-columns:repeat(var(--df-grid-columns),1fr)}.df-row.df-row-mobile-keep-cols>*{grid-column:revert!important}}@media(min-width:577px)and (max-width:768px){.df-form{--df-grid-gap: var(--df-grid-gap-md);--df-grid-row-gap: var(--df-grid-row-gap-md)}.df-row{--df-grid-columns: 6}.df-col-sm-1{grid-column:span 1}.df-col-sm-2{grid-column:span 2}.df-col-sm-3{grid-column:span 3}.df-col-sm-4{grid-column:span 4}.df-col-sm-5{grid-column:span 5}.df-col-sm-6{grid-column:span 6}.df-col-sm-full{grid-column:1/-1}}@media(min-width:769px)and (max-width:992px){.df-form{--df-grid-gap: var(--df-grid-gap-lg);--df-grid-row-gap: var(--df-grid-row-gap-lg)}.df-col-md-1{grid-column:span 1}.df-col-md-2{grid-column:span 2}.df-col-md-3{grid-column:span 3}.df-col-md-4{grid-column:span 4}.df-col-md-5{grid-column:span 5}.df-col-md-6{grid-column:span 6}.df-col-md-7{grid-column:span 7}.df-col-md-8{grid-column:span 8}.df-col-md-9{grid-column:span 9}.df-col-md-10{grid-column:span 10}.df-col-md-11{grid-column:span 11}.df-col-md-12{grid-column:span 12}.df-col-md-full{grid-column:1/-1}}@media(min-width:993px){.df-form{--df-grid-gap: var(--df-grid-gap-xl);--df-grid-row-gap: var(--df-grid-row-gap-xl)}.df-col-lg-1{grid-column:span 1}.df-col-lg-2{grid-column:span 2}.df-col-lg-3{grid-column:span 3}.df-col-lg-4{grid-column:span 4}.df-col-lg-5{grid-column:span 5}.df-col-lg-6{grid-column:span 6}.df-col-lg-7{grid-column:span 7}.df-col-lg-8{grid-column:span 8}.df-col-lg-9{grid-column:span 9}.df-col-lg-10{grid-column:span 10}.df-col-lg-11{grid-column:span 11}.df-col-lg-12{grid-column:span 12}.df-col-lg-full{grid-column:1/-1}}.df-gap-none{--df-grid-gap: 0}.df-gap-xs{--df-grid-gap: .25rem}.df-gap-sm{--df-grid-gap: .5rem}.df-gap-md{--df-grid-gap: 1rem}.df-gap-lg{--df-grid-gap: 1.5rem}.df-gap-xl{--df-grid-gap: 2rem}.df-row-gap-none{--df-grid-row-gap: 0}.df-row-gap-xs{--df-grid-row-gap: .25rem}.df-row-gap-sm{--df-grid-row-gap: .5rem}.df-row-gap-md{--df-grid-row-gap: 1rem}.df-row-gap-lg{--df-grid-row-gap: 1.5rem}.df-row-gap-xl{--df-grid-row-gap: 2rem}.df-field{display:block;width:100%;min-width:0;overflow:hidden;margin:0}.df-group,.df-page{display:block;width:100%}.df-form.disabled,.df-row.disabled,.df-field.disabled{opacity:.6;pointer-events:none}.df-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-sr-only-focusable:focus,.df-sr-only-focusable:active{position:static;width:auto;height:auto;padding:inherit;margin:inherit;overflow:visible;clip:auto;white-space:normal}.df-live-region{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-form :focus-visible{outline:2px solid var(--df-focus-color, #005fcc);outline-offset:2px}.df-group:focus-within{outline:1px dashed var(--df-focus-color, #005fcc);outline-offset:4px}:host{display:flex;flex-wrap:wrap;gap:var(--df-grid-gap);align-items:flex-start;width:100%}:host ::ng-deep>*:not([class*=df-col-]){flex:0 0 auto}:host ::ng-deep>.df-col-1{flex:0 0 calc(8.3333333333% - var(--df-grid-gap) * .9166666667);max-width:calc(8.3333333333% - var(--df-grid-gap) * .9166666667)}:host ::ng-deep>.df-col-2{flex:0 0 calc(16.6666666667% - var(--df-grid-gap) * .8333333333);max-width:calc(16.6666666667% - var(--df-grid-gap) * .8333333333)}:host ::ng-deep>.df-col-3{flex:0 0 calc(25% - var(--df-grid-gap) * .75);max-width:calc(25% - var(--df-grid-gap) * .75)}:host ::ng-deep>.df-col-4{flex:0 0 calc(33.3333333333% - var(--df-grid-gap) * .6666666667);max-width:calc(33.3333333333% - var(--df-grid-gap) * .6666666667)}:host ::ng-deep>.df-col-5{flex:0 0 calc(41.6666666667% - var(--df-grid-gap) * .5833333333);max-width:calc(41.6666666667% - var(--df-grid-gap) * .5833333333)}:host ::ng-deep>.df-col-6{flex:0 0 calc(50% - var(--df-grid-gap) * .5);max-width:calc(50% - var(--df-grid-gap) * .5)}:host ::ng-deep>.df-col-7{flex:0 0 calc(58.3333333333% - var(--df-grid-gap) * .4166666667);max-width:calc(58.3333333333% - var(--df-grid-gap) * .4166666667)}:host ::ng-deep>.df-col-8{flex:0 0 calc(66.6666666667% - var(--df-grid-gap) * .3333333333);max-width:calc(66.6666666667% - var(--df-grid-gap) * .3333333333)}:host ::ng-deep>.df-col-9{flex:0 0 calc(75% - var(--df-grid-gap) * .25);max-width:calc(75% - var(--df-grid-gap) * .25)}:host ::ng-deep>.df-col-10{flex:0 0 calc(83.3333333333% - var(--df-grid-gap) * .1666666667);max-width:calc(83.3333333333% - var(--df-grid-gap) * .1666666667)}:host ::ng-deep>.df-col-11{flex:0 0 calc(91.6666666667% - var(--df-grid-gap) * .0833333333);max-width:calc(91.6666666667% - var(--df-grid-gap) * .0833333333)}:host ::ng-deep>.df-col-12{flex:0 0 calc(100% - var(--df-grid-gap) * 0);max-width:calc(100% - var(--df-grid-gap) * 0)}:host.df-gap-custom{--df-grid-gap: var(--row-custom-gap, 1rem)}@media(max-width:576px){:host.df-row-mobile-stack{flex-direction:column}:host.df-row-mobile-stack ::ng-deep>*{flex:0 0 100%!important;max-width:100%!important}}@media(min-width:577px)and (max-width:768px){:host.df-row-tablet-2col ::ng-deep>*{flex:0 0 calc(50% - var(--df-grid-gap) / 2);max-width:calc(50% - var(--df-grid-gap) / 2)}}@media(max-width:576px){:host{flex-direction:column}:host ::ng-deep>*{flex:0 0 100%!important;max-width:100%!important}:host.df-row-mobile-keep-cols{flex-direction:row}:host.df-row-mobile-keep-cols ::ng-deep>*:not([class*=df-col-]){flex:0 0 auto!important;max-width:none!important}}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
6377
6379
|
}
|
|
6378
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
6380
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: RowFieldComponent, decorators: [{
|
|
6379
6381
|
type: Component,
|
|
6380
6382
|
args: [{ selector: 'div[row-field]', imports: [NgComponentOutlet], template: `
|
|
6381
6383
|
@for (field of resolvedFields(); track field.key) {
|
|
@@ -6411,7 +6413,7 @@ class GroupFieldComponent {
|
|
|
6411
6413
|
parentFieldSignalContext = inject(FIELD_SIGNAL_CONTEXT);
|
|
6412
6414
|
injector = inject(Injector);
|
|
6413
6415
|
eventBus = inject(EventBus);
|
|
6414
|
-
logger = inject(
|
|
6416
|
+
logger = inject(DynamicFormLogger);
|
|
6415
6417
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
6416
6418
|
// Memoized Functions
|
|
6417
6419
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -6490,7 +6492,7 @@ class GroupFieldComponent {
|
|
|
6490
6492
|
const groupKey = this.field().key;
|
|
6491
6493
|
const child = parentForm[groupKey];
|
|
6492
6494
|
if (!child) {
|
|
6493
|
-
throw new
|
|
6495
|
+
throw new DynamicFormError(`Group field "${groupKey}" not found in parent form. ` + `Ensure the parent form schema includes this group field.`);
|
|
6494
6496
|
}
|
|
6495
6497
|
return child;
|
|
6496
6498
|
}, ...(ngDevMode ? [{ debugName: "nestedFieldTree" }] : []));
|
|
@@ -6545,14 +6547,14 @@ class GroupFieldComponent {
|
|
|
6545
6547
|
}
|
|
6546
6548
|
});
|
|
6547
6549
|
}
|
|
6548
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
6549
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.
|
|
6550
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: GroupFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6551
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: GroupFieldComponent, isStandalone: true, selector: "fieldset[group-field]", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { validityChange: "validityChange", dirtyChange: "dirtyChange", submitted: "submitted" }, host: { attributes: { "role": "group" }, properties: { "class.disabled": "disabled()", "id": "`${key()}`", "attr.data-testid": "key()" }, classAttribute: "df-field df-group" }, ngImport: i0, template: `
|
|
6550
6552
|
@for (field of resolvedFields(); track field.key) {
|
|
6551
6553
|
<ng-container *ngComponentOutlet="field.component; injector: field.injector; inputs: field.inputs()" />
|
|
6552
6554
|
}
|
|
6553
6555
|
`, isInline: true, styles: [":host,.df-form{--df-grid-columns: 12;--df-grid-gap: .5rem;--df-grid-row-gap: .5rem;--df-breakpoint-sm: 576px;--df-breakpoint-md: 768px;--df-breakpoint-lg: 992px;--df-breakpoint-xl: 1200px;--df-grid-gap-sm: .5rem;--df-grid-gap-md: .5rem;--df-grid-gap-lg: .5rem;--df-grid-gap-xl: .5rem;--df-grid-row-gap-sm: .5rem;--df-grid-row-gap-md: .5rem;--df-grid-row-gap-lg: .5rem;--df-grid-row-gap-xl: .5rem;--df-group-gap: var(--df-grid-gap);--df-group-padding: var(--df-grid-gap)}.df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap);width:100%}.df-form>*{grid-column:1/-1}.df-row{display:grid;grid-template-columns:repeat(var(--df-grid-columns, 12),1fr);gap:var(--df-grid-gap);align-items:start;width:100%}.df-row>*:not([class*=df-col-]){grid-column:1/-1}.df-col-1{grid-column:span 1}.df-col-2{grid-column:span 2}.df-col-3{grid-column:span 3}.df-col-4{grid-column:span 4}.df-col-5{grid-column:span 5}.df-col-6{grid-column:span 6}.df-col-7{grid-column:span 7}.df-col-8{grid-column:span 8}.df-col-9{grid-column:span 9}.df-col-10{grid-column:span 10}.df-col-11{grid-column:span 11}.df-col-12{grid-column:span 12}.df-col-auto{grid-column:span auto;width:auto}.df-col-full{grid-column:1/-1}.df-col-start-1{grid-column-start:1}.df-col-start-2{grid-column-start:2}.df-col-start-3{grid-column-start:3}.df-col-start-4{grid-column-start:4}.df-col-start-5{grid-column-start:5}.df-col-start-6{grid-column-start:6}.df-col-start-7{grid-column-start:7}.df-col-start-8{grid-column-start:8}.df-col-start-9{grid-column-start:9}.df-col-start-10{grid-column-start:10}.df-col-start-11{grid-column-start:11}.df-col-start-12{grid-column-start:12}.df-col-end-1{grid-column-end:1}.df-col-end-2{grid-column-end:2}.df-col-end-3{grid-column-end:3}.df-col-end-4{grid-column-end:4}.df-col-end-5{grid-column-end:5}.df-col-end-6{grid-column-end:6}.df-col-end-7{grid-column-end:7}.df-col-end-8{grid-column-end:8}.df-col-end-9{grid-column-end:9}.df-col-end-10{grid-column-end:10}.df-col-end-11{grid-column-end:11}.df-col-end-12{grid-column-end:12}.df-col-end-13{grid-column-end:13}@media(max-width:576px){.df-form{--df-grid-gap: var(--df-grid-gap-sm);--df-grid-row-gap: var(--df-grid-row-gap-sm)}.df-row{grid-template-columns:1fr}.df-row>*{grid-column:1/-1!important}.df-row.df-row-mobile-keep-cols{grid-template-columns:repeat(var(--df-grid-columns),1fr)}.df-row.df-row-mobile-keep-cols>*{grid-column:revert!important}}@media(min-width:577px)and (max-width:768px){.df-form{--df-grid-gap: var(--df-grid-gap-md);--df-grid-row-gap: var(--df-grid-row-gap-md)}.df-row{--df-grid-columns: 6}.df-col-sm-1{grid-column:span 1}.df-col-sm-2{grid-column:span 2}.df-col-sm-3{grid-column:span 3}.df-col-sm-4{grid-column:span 4}.df-col-sm-5{grid-column:span 5}.df-col-sm-6{grid-column:span 6}.df-col-sm-full{grid-column:1/-1}}@media(min-width:769px)and (max-width:992px){.df-form{--df-grid-gap: var(--df-grid-gap-lg);--df-grid-row-gap: var(--df-grid-row-gap-lg)}.df-col-md-1{grid-column:span 1}.df-col-md-2{grid-column:span 2}.df-col-md-3{grid-column:span 3}.df-col-md-4{grid-column:span 4}.df-col-md-5{grid-column:span 5}.df-col-md-6{grid-column:span 6}.df-col-md-7{grid-column:span 7}.df-col-md-8{grid-column:span 8}.df-col-md-9{grid-column:span 9}.df-col-md-10{grid-column:span 10}.df-col-md-11{grid-column:span 11}.df-col-md-12{grid-column:span 12}.df-col-md-full{grid-column:1/-1}}@media(min-width:993px){.df-form{--df-grid-gap: var(--df-grid-gap-xl);--df-grid-row-gap: var(--df-grid-row-gap-xl)}.df-col-lg-1{grid-column:span 1}.df-col-lg-2{grid-column:span 2}.df-col-lg-3{grid-column:span 3}.df-col-lg-4{grid-column:span 4}.df-col-lg-5{grid-column:span 5}.df-col-lg-6{grid-column:span 6}.df-col-lg-7{grid-column:span 7}.df-col-lg-8{grid-column:span 8}.df-col-lg-9{grid-column:span 9}.df-col-lg-10{grid-column:span 10}.df-col-lg-11{grid-column:span 11}.df-col-lg-12{grid-column:span 12}.df-col-lg-full{grid-column:1/-1}}.df-gap-none{--df-grid-gap: 0}.df-gap-xs{--df-grid-gap: .25rem}.df-gap-sm{--df-grid-gap: .5rem}.df-gap-md{--df-grid-gap: 1rem}.df-gap-lg{--df-grid-gap: 1.5rem}.df-gap-xl{--df-grid-gap: 2rem}.df-row-gap-none{--df-grid-row-gap: 0}.df-row-gap-xs{--df-grid-row-gap: .25rem}.df-row-gap-sm{--df-grid-row-gap: .5rem}.df-row-gap-md{--df-grid-row-gap: 1rem}.df-row-gap-lg{--df-grid-row-gap: 1.5rem}.df-row-gap-xl{--df-grid-row-gap: 2rem}.df-field{display:block;width:100%;min-width:0;overflow:hidden;margin:0}.df-group,.df-page{display:block;width:100%}.df-form.disabled,.df-row.disabled,.df-field.disabled{opacity:.6;pointer-events:none}.df-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-sr-only-focusable:focus,.df-sr-only-focusable:active{position:static;width:auto;height:auto;padding:inherit;margin:inherit;overflow:visible;clip:auto;white-space:normal}.df-live-region{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-form :focus-visible{outline:2px solid var(--df-focus-color, #005fcc);outline-offset:2px}.df-group:focus-within{outline:1px dashed var(--df-focus-color, #005fcc);outline-offset:4px}:host{border:none;margin:0;padding:0;min-width:0;display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}:host>*{grid-column:1/-1}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
6554
6556
|
}
|
|
6555
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
6557
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: GroupFieldComponent, decorators: [{
|
|
6556
6558
|
type: Component,
|
|
6557
6559
|
args: [{ selector: 'fieldset[group-field]', imports: [NgComponentOutlet], template: `
|
|
6558
6560
|
@for (field of resolvedFields(); track field.key) {
|
|
@@ -6771,14 +6773,13 @@ function resolveArrayItem(options) {
|
|
|
6771
6773
|
});
|
|
6772
6774
|
return {
|
|
6773
6775
|
id: itemId,
|
|
6774
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6775
6776
|
component: component,
|
|
6776
6777
|
injector,
|
|
6777
6778
|
inputs,
|
|
6778
6779
|
};
|
|
6779
6780
|
}), catchError((error) => {
|
|
6780
6781
|
if (!destroyRef.destroyed) {
|
|
6781
|
-
const logger = parentInjector.get(
|
|
6782
|
+
const logger = parentInjector.get(DynamicFormLogger);
|
|
6782
6783
|
logger.error(`Failed to load component for field type '${template.type}' at index ${index} ` +
|
|
6783
6784
|
`within array '${arrayField.key}'. Ensure the field type is registered in your field registry.`, error);
|
|
6784
6785
|
}
|
|
@@ -6802,7 +6803,7 @@ class ArrayFieldComponent {
|
|
|
6802
6803
|
parentFieldSignalContext = inject(FIELD_SIGNAL_CONTEXT);
|
|
6803
6804
|
parentInjector = inject(Injector);
|
|
6804
6805
|
eventBus = inject(EventBus);
|
|
6805
|
-
logger = inject(
|
|
6806
|
+
logger = inject(DynamicFormLogger);
|
|
6806
6807
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
6807
6808
|
// Inputs
|
|
6808
6809
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -6978,14 +6979,14 @@ class ArrayFieldComponent {
|
|
|
6978
6979
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6979
6980
|
parentForm().value.set({ ...currentValue, [arrayKey]: newArray });
|
|
6980
6981
|
}
|
|
6981
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
6982
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.
|
|
6982
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ArrayFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6983
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ArrayFieldComponent, isStandalone: true, selector: "array-field", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "id": "`${key()}`", "attr.data-testid": "key()" }, classAttribute: "df-field df-array" }, ngImport: i0, template: `
|
|
6983
6984
|
@for (item of resolvedItems(); track item.id) {
|
|
6984
6985
|
<ng-container *ngComponentOutlet="item.component; injector: item.injector; inputs: item.inputs()" />
|
|
6985
6986
|
}
|
|
6986
6987
|
`, isInline: true, styles: [":host,.df-form{--df-grid-columns: 12;--df-grid-gap: .5rem;--df-grid-row-gap: .5rem;--df-breakpoint-sm: 576px;--df-breakpoint-md: 768px;--df-breakpoint-lg: 992px;--df-breakpoint-xl: 1200px;--df-grid-gap-sm: .5rem;--df-grid-gap-md: .5rem;--df-grid-gap-lg: .5rem;--df-grid-gap-xl: .5rem;--df-grid-row-gap-sm: .5rem;--df-grid-row-gap-md: .5rem;--df-grid-row-gap-lg: .5rem;--df-grid-row-gap-xl: .5rem;--df-group-gap: var(--df-grid-gap);--df-group-padding: var(--df-grid-gap)}.df-form{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap);width:100%}.df-form>*{grid-column:1/-1}.df-row{display:grid;grid-template-columns:repeat(var(--df-grid-columns, 12),1fr);gap:var(--df-grid-gap);align-items:start;width:100%}.df-row>*:not([class*=df-col-]){grid-column:1/-1}.df-col-1{grid-column:span 1}.df-col-2{grid-column:span 2}.df-col-3{grid-column:span 3}.df-col-4{grid-column:span 4}.df-col-5{grid-column:span 5}.df-col-6{grid-column:span 6}.df-col-7{grid-column:span 7}.df-col-8{grid-column:span 8}.df-col-9{grid-column:span 9}.df-col-10{grid-column:span 10}.df-col-11{grid-column:span 11}.df-col-12{grid-column:span 12}.df-col-auto{grid-column:span auto;width:auto}.df-col-full{grid-column:1/-1}.df-col-start-1{grid-column-start:1}.df-col-start-2{grid-column-start:2}.df-col-start-3{grid-column-start:3}.df-col-start-4{grid-column-start:4}.df-col-start-5{grid-column-start:5}.df-col-start-6{grid-column-start:6}.df-col-start-7{grid-column-start:7}.df-col-start-8{grid-column-start:8}.df-col-start-9{grid-column-start:9}.df-col-start-10{grid-column-start:10}.df-col-start-11{grid-column-start:11}.df-col-start-12{grid-column-start:12}.df-col-end-1{grid-column-end:1}.df-col-end-2{grid-column-end:2}.df-col-end-3{grid-column-end:3}.df-col-end-4{grid-column-end:4}.df-col-end-5{grid-column-end:5}.df-col-end-6{grid-column-end:6}.df-col-end-7{grid-column-end:7}.df-col-end-8{grid-column-end:8}.df-col-end-9{grid-column-end:9}.df-col-end-10{grid-column-end:10}.df-col-end-11{grid-column-end:11}.df-col-end-12{grid-column-end:12}.df-col-end-13{grid-column-end:13}@media(max-width:576px){.df-form{--df-grid-gap: var(--df-grid-gap-sm);--df-grid-row-gap: var(--df-grid-row-gap-sm)}.df-row{grid-template-columns:1fr}.df-row>*{grid-column:1/-1!important}.df-row.df-row-mobile-keep-cols{grid-template-columns:repeat(var(--df-grid-columns),1fr)}.df-row.df-row-mobile-keep-cols>*{grid-column:revert!important}}@media(min-width:577px)and (max-width:768px){.df-form{--df-grid-gap: var(--df-grid-gap-md);--df-grid-row-gap: var(--df-grid-row-gap-md)}.df-row{--df-grid-columns: 6}.df-col-sm-1{grid-column:span 1}.df-col-sm-2{grid-column:span 2}.df-col-sm-3{grid-column:span 3}.df-col-sm-4{grid-column:span 4}.df-col-sm-5{grid-column:span 5}.df-col-sm-6{grid-column:span 6}.df-col-sm-full{grid-column:1/-1}}@media(min-width:769px)and (max-width:992px){.df-form{--df-grid-gap: var(--df-grid-gap-lg);--df-grid-row-gap: var(--df-grid-row-gap-lg)}.df-col-md-1{grid-column:span 1}.df-col-md-2{grid-column:span 2}.df-col-md-3{grid-column:span 3}.df-col-md-4{grid-column:span 4}.df-col-md-5{grid-column:span 5}.df-col-md-6{grid-column:span 6}.df-col-md-7{grid-column:span 7}.df-col-md-8{grid-column:span 8}.df-col-md-9{grid-column:span 9}.df-col-md-10{grid-column:span 10}.df-col-md-11{grid-column:span 11}.df-col-md-12{grid-column:span 12}.df-col-md-full{grid-column:1/-1}}@media(min-width:993px){.df-form{--df-grid-gap: var(--df-grid-gap-xl);--df-grid-row-gap: var(--df-grid-row-gap-xl)}.df-col-lg-1{grid-column:span 1}.df-col-lg-2{grid-column:span 2}.df-col-lg-3{grid-column:span 3}.df-col-lg-4{grid-column:span 4}.df-col-lg-5{grid-column:span 5}.df-col-lg-6{grid-column:span 6}.df-col-lg-7{grid-column:span 7}.df-col-lg-8{grid-column:span 8}.df-col-lg-9{grid-column:span 9}.df-col-lg-10{grid-column:span 10}.df-col-lg-11{grid-column:span 11}.df-col-lg-12{grid-column:span 12}.df-col-lg-full{grid-column:1/-1}}.df-gap-none{--df-grid-gap: 0}.df-gap-xs{--df-grid-gap: .25rem}.df-gap-sm{--df-grid-gap: .5rem}.df-gap-md{--df-grid-gap: 1rem}.df-gap-lg{--df-grid-gap: 1.5rem}.df-gap-xl{--df-grid-gap: 2rem}.df-row-gap-none{--df-grid-row-gap: 0}.df-row-gap-xs{--df-grid-row-gap: .25rem}.df-row-gap-sm{--df-grid-row-gap: .5rem}.df-row-gap-md{--df-grid-row-gap: 1rem}.df-row-gap-lg{--df-grid-row-gap: 1.5rem}.df-row-gap-xl{--df-grid-row-gap: 2rem}.df-field{display:block;width:100%;min-width:0;overflow:hidden;margin:0}.df-group,.df-page{display:block;width:100%}.df-form.disabled,.df-row.disabled,.df-field.disabled{opacity:.6;pointer-events:none}.df-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-sr-only-focusable:focus,.df-sr-only-focusable:active{position:static;width:auto;height:auto;padding:inherit;margin:inherit;overflow:visible;clip:auto;white-space:normal}.df-live-region{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.df-form :focus-visible{outline:2px solid var(--df-focus-color, #005fcc);outline-offset:2px}.df-group:focus-within{outline:1px dashed var(--df-focus-color, #005fcc);outline-offset:4px}:host{display:grid;grid-template-columns:1fr;gap:var(--df-grid-row-gap, .5rem);width:100%}:host>*{grid-column:1/-1}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
6987
6988
|
}
|
|
6988
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
6989
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ArrayFieldComponent, decorators: [{
|
|
6989
6990
|
type: Component,
|
|
6990
6991
|
args: [{ selector: 'array-field', imports: [NgComponentOutlet], template: `
|
|
6991
6992
|
@for (item of resolvedItems(); track item.id) {
|
|
@@ -7132,5 +7133,5 @@ function extractErrorParams(error) {
|
|
|
7132
7133
|
* Generated bundle index. Do not edit.
|
|
7133
7134
|
*/
|
|
7134
7135
|
|
|
7135
|
-
export {
|
|
7136
|
-
//# sourceMappingURL=ng-forge-dynamic-forms-ng-forge-dynamic-forms-
|
|
7136
|
+
export { BUILT_IN_FIELDS as $, AddArrayItemEvent as A, baseFieldMapper as B, ConsoleLogger as C, DynamicFormLogger as D, EventBus as E, FormClearEvent as F, buildBaseInputs as G, arrayFieldMapper as H, groupFieldMapper as I, pageFieldMapper as J, rowFieldMapper as K, textFieldMapper as L, FIELD_REGISTRY as M, NoopLogger as N, ARRAY_CONTEXT as O, PageChangeEvent as P, FIELD_SIGNAL_CONTEXT as Q, RemoveArrayItemEvent as R, SubmitEvent as S, dynamicTextToObservable as T, ArrayFieldComponent as U, GroupFieldComponent as V, RowFieldComponent as W, applyValidator as X, applyValidators as Y, getArrayLength as Z, resolveTokens as _, DynamicTextPipe as a, omit as a0, interpolateParams as a1, DynamicForm as b, createFieldResolutionPipe as c, derivedFromDeferred as d, emitComponentInitialized as e, isFormStateCondition as f, resolveNextButtonDisabled as g, isCheckedField as h, injectFieldRegistry as i, isValueField as j, isRowField as k, isArrayField as l, isContainerField as m, isDisplayOnlyField as n, isGroupField as o, provideDynamicForm as p, isLeafField as q, resolveSubmitButtonDisabled as r, isPageField as s, isValueBearingField as t, FormResetEvent as u, validatePageNesting as v, withLoggerConfig as w, NextPageEvent as x, PreviousPageEvent as y, DynamicFormError as z };
|
|
7137
|
+
//# sourceMappingURL=ng-forge-dynamic-forms-ng-forge-dynamic-forms-CZBdtTAU.mjs.map
|