@syncfusion/ej2-form-renderer 34.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -0
- package/common.d.ts +4 -0
- package/common.js +4 -0
- package/dist/ej2-form-renderer.umd.min.js +11 -0
- package/dist/ej2-form-renderer.umd.min.js.map +1 -0
- package/dist/es6/ej2-form-renderer.es2015.js +6787 -0
- package/dist/es6/ej2-form-renderer.es2015.js.map +1 -0
- package/dist/es6/ej2-form-renderer.es5.js +7055 -0
- package/dist/es6/ej2-form-renderer.es5.js.map +1 -0
- package/dist/global/ej2-form-renderer.min.js +11 -0
- package/dist/global/ej2-form-renderer.min.js.map +1 -0
- package/dist/global/index.d.ts +14 -0
- package/form-renderer.d.ts +4 -0
- package/form-renderer.js +4 -0
- package/index.d.ts +4 -0
- package/index.js +4 -0
- package/license +10 -0
- package/package.json +41 -0
- package/src/common/checkbox-group.d.ts +55 -0
- package/src/common/checkbox-group.js +241 -0
- package/src/common/conditional-rule-engine.d.ts +241 -0
- package/src/common/conditional-rule-engine.js +505 -0
- package/src/common/custom-validation-engine.d.ts +57 -0
- package/src/common/custom-validation-engine.js +152 -0
- package/src/common/datagrid-component.d.ts +156 -0
- package/src/common/datagrid-component.js +410 -0
- package/src/common/expressions/dependency-tracker.d.ts +29 -0
- package/src/common/expressions/dependency-tracker.js +137 -0
- package/src/common/expressions/expression-engine.d.ts +94 -0
- package/src/common/expressions/expression-engine.js +514 -0
- package/src/common/expressions/expression-parser.d.ts +40 -0
- package/src/common/expressions/expression-parser.js +270 -0
- package/src/common/expressions/grid-expressions.d.ts +24 -0
- package/src/common/expressions/grid-expressions.js +92 -0
- package/src/common/expressions/index.d.ts +4 -0
- package/src/common/expressions/index.js +4 -0
- package/src/common/index.d.ts +10 -0
- package/src/common/index.js +10 -0
- package/src/common/json-converter.d.ts +945 -0
- package/src/common/json-converter.js +1110 -0
- package/src/common/minmax-rule-engine.d.ts +105 -0
- package/src/common/minmax-rule-engine.js +380 -0
- package/src/common/option-fetcher.d.ts +30 -0
- package/src/common/option-fetcher.js +139 -0
- package/src/common/parser.d.ts +98 -0
- package/src/common/parser.js +788 -0
- package/src/common/static-html-renderer.d.ts +45 -0
- package/src/common/static-html-renderer.js +77 -0
- package/src/common/utils.d.ts +116 -0
- package/src/common/utils.js +484 -0
- package/src/form-renderer/form-renderer-model.d.ts +65 -0
- package/src/form-renderer/form-renderer.d.ts +425 -0
- package/src/form-renderer/form-renderer.js +2277 -0
- package/src/form-renderer/index.d.ts +6 -0
- package/src/form-renderer/index.js +5 -0
- package/src/form-renderer/render-form-field.d.ts +31 -0
- package/src/form-renderer/render-form-field.js +816 -0
- package/src/form-renderer/types/form-schema.d.ts +452 -0
- package/src/form-renderer/types/form-schema.js +32 -0
- package/src/global.js +1 -0
- package/src/index.d.ts +4 -0
- package/src/index.js +4 -0
- package/styles/bds-lite.css +599 -0
- package/styles/bds-lite.scss +2 -0
- package/styles/bds.css +599 -0
- package/styles/bds.scss +2 -0
- package/styles/bootstrap-dark-lite.css +608 -0
- package/styles/bootstrap-dark-lite.scss +2 -0
- package/styles/bootstrap-dark.css +608 -0
- package/styles/bootstrap-dark.scss +2 -0
- package/styles/bootstrap-lite.css +595 -0
- package/styles/bootstrap-lite.scss +2 -0
- package/styles/bootstrap.css +595 -0
- package/styles/bootstrap.scss +2 -0
- package/styles/bootstrap4-lite.css +595 -0
- package/styles/bootstrap4-lite.scss +2 -0
- package/styles/bootstrap4.css +595 -0
- package/styles/bootstrap4.scss +2 -0
- package/styles/bootstrap5-dark-lite.css +595 -0
- package/styles/bootstrap5-dark-lite.scss +2 -0
- package/styles/bootstrap5-dark.css +595 -0
- package/styles/bootstrap5-dark.scss +2 -0
- package/styles/bootstrap5-lite.css +595 -0
- package/styles/bootstrap5-lite.scss +2 -0
- package/styles/bootstrap5.3-lite.css +679 -0
- package/styles/bootstrap5.3-lite.scss +2 -0
- package/styles/bootstrap5.3.css +679 -0
- package/styles/bootstrap5.3.scss +2 -0
- package/styles/bootstrap5.css +595 -0
- package/styles/bootstrap5.scss +2 -0
- package/styles/fabric-dark-lite.css +608 -0
- package/styles/fabric-dark-lite.scss +2 -0
- package/styles/fabric-dark.css +608 -0
- package/styles/fabric-dark.scss +2 -0
- package/styles/fabric-lite.css +595 -0
- package/styles/fabric-lite.scss +2 -0
- package/styles/fabric.css +595 -0
- package/styles/fabric.scss +2 -0
- package/styles/fluent-dark-lite.css +595 -0
- package/styles/fluent-dark-lite.scss +2 -0
- package/styles/fluent-dark.css +595 -0
- package/styles/fluent-dark.scss +2 -0
- package/styles/fluent-lite.css +595 -0
- package/styles/fluent-lite.scss +2 -0
- package/styles/fluent.css +595 -0
- package/styles/fluent.scss +2 -0
- package/styles/fluent2-lite.css +681 -0
- package/styles/fluent2-lite.scss +2 -0
- package/styles/fluent2.css +681 -0
- package/styles/fluent2.scss +2 -0
- package/styles/form-renderer/_all.scss +2 -0
- package/styles/form-renderer/_bds-definition.scss +8 -0
- package/styles/form-renderer/_bootstrap-dark-definition.scss +8 -0
- package/styles/form-renderer/_bootstrap-definition.scss +8 -0
- package/styles/form-renderer/_bootstrap4-definition.scss +8 -0
- package/styles/form-renderer/_bootstrap5-dark-definition.scss +8 -0
- package/styles/form-renderer/_bootstrap5-definition.scss +8 -0
- package/styles/form-renderer/_bootstrap5.3-definition.scss +8 -0
- package/styles/form-renderer/_fabric-dark-definition.scss +8 -0
- package/styles/form-renderer/_fabric-definition.scss +8 -0
- package/styles/form-renderer/_fluent-dark-definition.scss +8 -0
- package/styles/form-renderer/_fluent-definition.scss +8 -0
- package/styles/form-renderer/_fluent2-definition.scss +8 -0
- package/styles/form-renderer/_highcontrast-definition.scss +8 -0
- package/styles/form-renderer/_highcontrast-light-definition.scss +8 -0
- package/styles/form-renderer/_layout.scss +480 -0
- package/styles/form-renderer/_material-dark-definition.scss +8 -0
- package/styles/form-renderer/_material-definition.scss +8 -0
- package/styles/form-renderer/_material3-dark-definition.scss +8 -0
- package/styles/form-renderer/_material3-definition.scss +8 -0
- package/styles/form-renderer/_mixin.scss +26 -0
- package/styles/form-renderer/_tailwind-dark-definition.scss +8 -0
- package/styles/form-renderer/_tailwind-definition.scss +8 -0
- package/styles/form-renderer/_tailwind3-definition.scss +8 -0
- package/styles/form-renderer/_theme.scss +69 -0
- package/styles/form-renderer/_variables.scss +2 -0
- package/styles/form-renderer/bds.css +599 -0
- package/styles/form-renderer/bds.scss +2 -0
- package/styles/form-renderer/bootstrap-dark.css +608 -0
- package/styles/form-renderer/bootstrap-dark.scss +2 -0
- package/styles/form-renderer/bootstrap.css +595 -0
- package/styles/form-renderer/bootstrap.scss +2 -0
- package/styles/form-renderer/bootstrap4.css +595 -0
- package/styles/form-renderer/bootstrap4.scss +2 -0
- package/styles/form-renderer/bootstrap5-dark.css +595 -0
- package/styles/form-renderer/bootstrap5-dark.scss +2 -0
- package/styles/form-renderer/bootstrap5.3.css +679 -0
- package/styles/form-renderer/bootstrap5.3.scss +2 -0
- package/styles/form-renderer/bootstrap5.css +595 -0
- package/styles/form-renderer/bootstrap5.scss +2 -0
- package/styles/form-renderer/fabric-dark.css +608 -0
- package/styles/form-renderer/fabric-dark.scss +2 -0
- package/styles/form-renderer/fabric.css +595 -0
- package/styles/form-renderer/fabric.scss +2 -0
- package/styles/form-renderer/fluent-dark.css +595 -0
- package/styles/form-renderer/fluent-dark.scss +2 -0
- package/styles/form-renderer/fluent.css +595 -0
- package/styles/form-renderer/fluent.scss +2 -0
- package/styles/form-renderer/fluent2.css +681 -0
- package/styles/form-renderer/fluent2.scss +2 -0
- package/styles/form-renderer/highcontrast-light.css +595 -0
- package/styles/form-renderer/highcontrast-light.scss +2 -0
- package/styles/form-renderer/highcontrast.css +608 -0
- package/styles/form-renderer/highcontrast.scss +2 -0
- package/styles/form-renderer/material-dark.css +608 -0
- package/styles/form-renderer/material-dark.scss +2 -0
- package/styles/form-renderer/material.css +595 -0
- package/styles/form-renderer/material.scss +2 -0
- package/styles/form-renderer/material3-dark.css +677 -0
- package/styles/form-renderer/material3-dark.scss +2 -0
- package/styles/form-renderer/material3.css +679 -0
- package/styles/form-renderer/material3.scss +2 -0
- package/styles/form-renderer/tailwind-dark.css +595 -0
- package/styles/form-renderer/tailwind-dark.scss +2 -0
- package/styles/form-renderer/tailwind.css +595 -0
- package/styles/form-renderer/tailwind.scss +2 -0
- package/styles/form-renderer/tailwind3.css +669 -0
- package/styles/form-renderer/tailwind3.scss +2 -0
- package/styles/highcontrast-light-lite.css +595 -0
- package/styles/highcontrast-light-lite.scss +2 -0
- package/styles/highcontrast-light.css +595 -0
- package/styles/highcontrast-light.scss +2 -0
- package/styles/highcontrast-lite.css +608 -0
- package/styles/highcontrast-lite.scss +2 -0
- package/styles/highcontrast.css +608 -0
- package/styles/highcontrast.scss +2 -0
- package/styles/material-dark-lite.css +608 -0
- package/styles/material-dark-lite.scss +2 -0
- package/styles/material-dark.css +608 -0
- package/styles/material-dark.scss +2 -0
- package/styles/material-lite.css +595 -0
- package/styles/material-lite.scss +2 -0
- package/styles/material.css +595 -0
- package/styles/material.scss +2 -0
- package/styles/material3-dark-lite.css +677 -0
- package/styles/material3-dark-lite.scss +2 -0
- package/styles/material3-dark.css +677 -0
- package/styles/material3-dark.scss +2 -0
- package/styles/material3-lite.css +679 -0
- package/styles/material3-lite.scss +2 -0
- package/styles/material3.css +679 -0
- package/styles/material3.scss +2 -0
- package/styles/tailwind-dark-lite.css +595 -0
- package/styles/tailwind-dark-lite.scss +2 -0
- package/styles/tailwind-dark.css +595 -0
- package/styles/tailwind-dark.scss +2 -0
- package/styles/tailwind-lite.css +595 -0
- package/styles/tailwind-lite.scss +2 -0
- package/styles/tailwind.css +595 -0
- package/styles/tailwind.scss +2 -0
- package/styles/tailwind3-lite.css +669 -0
- package/styles/tailwind3-lite.scss +2 -0
- package/styles/tailwind3.css +669 -0
- package/styles/tailwind3.scss +2 -0
|
@@ -0,0 +1,2277 @@
|
|
|
1
|
+
var __extends = (this && this.__extends) || (function () {
|
|
2
|
+
var extendStatics = function (d, b) {
|
|
3
|
+
extendStatics = Object.setPrototypeOf ||
|
|
4
|
+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
5
|
+
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
|
6
|
+
return extendStatics(d, b);
|
|
7
|
+
};
|
|
8
|
+
return function (d, b) {
|
|
9
|
+
extendStatics(d, b);
|
|
10
|
+
function __() { this.constructor = d; }
|
|
11
|
+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
12
|
+
};
|
|
13
|
+
})();
|
|
14
|
+
var __assign = (this && this.__assign) || function () {
|
|
15
|
+
__assign = Object.assign || function(t) {
|
|
16
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
17
|
+
s = arguments[i];
|
|
18
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
19
|
+
t[p] = s[p];
|
|
20
|
+
}
|
|
21
|
+
return t;
|
|
22
|
+
};
|
|
23
|
+
return __assign.apply(this, arguments);
|
|
24
|
+
};
|
|
25
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
26
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
27
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
28
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
29
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
30
|
+
};
|
|
31
|
+
import { Component, Property, Event, NotifyPropertyChanges, enableRipple, createElement, isNullOrUndefined, L10n, getComponent, addClass } from '@syncfusion/ej2-base';
|
|
32
|
+
import { Tab } from '@syncfusion/ej2-navigations';
|
|
33
|
+
import { Tooltip } from '@syncfusion/ej2-popups';
|
|
34
|
+
import { FormValidator } from '@syncfusion/ej2-inputs';
|
|
35
|
+
import { renderFormField } from './render-form-field';
|
|
36
|
+
import { mergeSchema } from '../common/json-converter';
|
|
37
|
+
import { UniversalJsonParser } from '../common/parser';
|
|
38
|
+
import { VALIDATION_REGEX, formatDateValues, splitWidths } from '../common/utils';
|
|
39
|
+
import StaticHtmlRenderer from '../common/static-html-renderer';
|
|
40
|
+
import { ConditionalRuleEngine } from '../common/conditional-rule-engine';
|
|
41
|
+
import { ExpressionEngine } from '../common/expressions/expression-engine';
|
|
42
|
+
import { MinMaxRuleEngine } from '../common/minmax-rule-engine';
|
|
43
|
+
import { CustomValidationEngine } from '../common/custom-validation-engine';
|
|
44
|
+
/**
|
|
45
|
+
* @private
|
|
46
|
+
*
|
|
47
|
+
* Internal input target types array to define a target.
|
|
48
|
+
*/
|
|
49
|
+
var inputTargetTypes = [
|
|
50
|
+
'textbox',
|
|
51
|
+
'number',
|
|
52
|
+
'checkbox',
|
|
53
|
+
'dropdown',
|
|
54
|
+
'multiselect',
|
|
55
|
+
'date',
|
|
56
|
+
'dateTime',
|
|
57
|
+
'time',
|
|
58
|
+
'dateRange',
|
|
59
|
+
'switch',
|
|
60
|
+
'rating',
|
|
61
|
+
'colorPicker',
|
|
62
|
+
'inputMask',
|
|
63
|
+
'fileUpload'
|
|
64
|
+
];
|
|
65
|
+
/**
|
|
66
|
+
* @private
|
|
67
|
+
*/
|
|
68
|
+
export var defaultLocale = {
|
|
69
|
+
"unsupportedComponent": "Unsupported Component type"
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* The FormRenderer component consumes a JSON form schema and renders a fully functional, Syncfusion-based form. It handles field mapping, conditional visibility/disabling, expression evaluation, custom validation, and data submission.
|
|
73
|
+
*
|
|
74
|
+
* ```typescript
|
|
75
|
+
* let form: FormRenderer = new FormRenderer({
|
|
76
|
+
* schema: formSchema,
|
|
77
|
+
* submit: (args: SubmitEventArgs) => console.log(args)
|
|
78
|
+
* });
|
|
79
|
+
* form.appendTo('#container');
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
var FormRenderer = /** @class */ (function (_super) {
|
|
83
|
+
__extends(FormRenderer, _super);
|
|
84
|
+
/**
|
|
85
|
+
* Constructor for creating the widget
|
|
86
|
+
*
|
|
87
|
+
* @param {FormRendererModel} options - Specifies the form renderer model
|
|
88
|
+
* @param {string|HTMLDivElement} element - Specifies the target element
|
|
89
|
+
*/
|
|
90
|
+
function FormRenderer(options, element) {
|
|
91
|
+
var _this = _super.call(this, options, element) || this;
|
|
92
|
+
_this.processedSchemaCache = { version: '1.0.0', components: [] };
|
|
93
|
+
/** Localization instance */
|
|
94
|
+
_this.l10n = new L10n('form-renderer', defaultLocale, _this.locale);
|
|
95
|
+
/**
|
|
96
|
+
* @private
|
|
97
|
+
*
|
|
98
|
+
* Minimal form-state container owned by the orchestrator. The previous
|
|
99
|
+
* vanilla-TS port delegated this to `FormStateBridge`; that bridge's
|
|
100
|
+
* `registeredFields` gate silently short-circuited every field to "no
|
|
101
|
+
* error" because the orchestrator never called `registerField`. The
|
|
102
|
+
* EJ2 native `FormValidator` (bound to `this.element`) now handles all
|
|
103
|
+
* rule evaluation, error-element placement, change/blur/submit wiring,
|
|
104
|
+
* and ARIA — exactly what `FormValidator` was designed for. This object
|
|
105
|
+
* is the orchestrator's authoritative values store; `errors` are
|
|
106
|
+
* mirrored from the FormValidator's `errorRules` for `renderField`'s
|
|
107
|
+
* initial-paint logic and for submit-time `isValid` checks.
|
|
108
|
+
*/
|
|
109
|
+
_this.formState = { values: Object.create(null), errors: Object.create(null) };
|
|
110
|
+
/** EJ2 native FormValidator bound to `this.element` (replaces FormStateBridge). */
|
|
111
|
+
_this.validator = null;
|
|
112
|
+
/** Map of input component instances keyed by `name` (or `${name}_${idx}`). */
|
|
113
|
+
_this.inputRefs = {};
|
|
114
|
+
/** Conditional value applied tracker (per-field). */
|
|
115
|
+
_this.conditionalValueApplied = {};
|
|
116
|
+
/** Expression engine + dependency graph + computed-field set. */
|
|
117
|
+
_this.expressionEngine = null;
|
|
118
|
+
_this.expressionDependencyGraph = {};
|
|
119
|
+
_this.expressionEvaluationInProgress = false;
|
|
120
|
+
_this.previousFormValues = {};
|
|
121
|
+
_this.computedFieldNames = new Set();
|
|
122
|
+
/** Custom validation engine instance. */
|
|
123
|
+
_this.customValidationEngine = new CustomValidationEngine();
|
|
124
|
+
/** Latest form values snapshot for custom validation (named-key context). */
|
|
125
|
+
_this.currentFormValues = {};
|
|
126
|
+
/** Whether initial values have been seeded once. */
|
|
127
|
+
_this.isInitialized = false;
|
|
128
|
+
/** Cached layout detection result. */
|
|
129
|
+
_this.hasLayoutComponentsCache = false;
|
|
130
|
+
/** Cached id→label map. */
|
|
131
|
+
_this.idToLabelCache = {};
|
|
132
|
+
/** Cached initial values (recomputed on schema change). */
|
|
133
|
+
_this.initialValuesCache = {};
|
|
134
|
+
/** Rendered child EJ2 instances (for destroy cleanup). */
|
|
135
|
+
_this.renderedInstances = [];
|
|
136
|
+
/** Tooltip instances keyed by field id so stale tooltips can be replaced promptly. */
|
|
137
|
+
_this.tooltipRefs = {};
|
|
138
|
+
/** `created` fired-once guard. */
|
|
139
|
+
_this.createdFired = false;
|
|
140
|
+
/**
|
|
141
|
+
* Coalescing guard for `scheduleRerender`. Prevents redundant full-form
|
|
142
|
+
* re-renders within a single microtask — mirrors React's batched-state
|
|
143
|
+
* semantics so that a user keystroke + expression cascade fire one render.
|
|
144
|
+
*/
|
|
145
|
+
_this.rerenderScheduled = false;
|
|
146
|
+
/**
|
|
147
|
+
* Set of `component.id`s that are referenced as the trigger field of any
|
|
148
|
+
* other component's `conditions` (`visibleWhen` / `hideWhen` /
|
|
149
|
+
* `disabledWhen` / `requiredWhen` / `readOnlyWhen` / `setValueWhen` /
|
|
150
|
+
* `choiceBasedField.primaryFieldId`). Only changes to these fields can
|
|
151
|
+
* affect visibility / disabled / required / valueWhen of any other
|
|
152
|
+
* field, so only they need to trigger a full-form re-render. Changes to
|
|
153
|
+
* fields *not* in this set are guaranteed to have no conditional side
|
|
154
|
+
* effects and safely skip the (expensive) full DOM teardown + rebuild.
|
|
155
|
+
* Built once per schema in `buildConditionalDependencyMap`.
|
|
156
|
+
*/
|
|
157
|
+
_this.conditionalTriggerFieldIds = new Set();
|
|
158
|
+
/**
|
|
159
|
+
* Maps `component.id` → the previously-rendered DOM subtree (form-group)
|
|
160
|
+
* for "expensive" components (dataGrid, dropdown, multiselect,
|
|
161
|
+
* checkboxGroup) that support remote data. During a full-form re-render
|
|
162
|
+
* triggered by an *unrelated* conditional change, these subtrees are
|
|
163
|
+
* detached (not destroyed) and re-attached in place, avoiding a re-fetch
|
|
164
|
+
* of their remote data sources and preventing the visible blink/reload.
|
|
165
|
+
* A subtree is *invalidated* (dropped from this map, forcing a fresh
|
|
166
|
+
* render) when a field it depends on (via its own `conditions` or
|
|
167
|
+
* `conditionalData`) is the one that changed.
|
|
168
|
+
*/
|
|
169
|
+
_this.preservedFields = new Map();
|
|
170
|
+
/**
|
|
171
|
+
* Per-consumer dependency map: `component.id` → set of trigger field ids
|
|
172
|
+
* that the consumer's own `conditions` / `conditionalData` /
|
|
173
|
+
* `choiceBasedField` reference. Used during `rerenderForm` to decide
|
|
174
|
+
* whether a preserved subtree must be invalidated (rebuilt) because the
|
|
175
|
+
* changed field is one of its own dependencies, or can be safely reused.
|
|
176
|
+
*/
|
|
177
|
+
_this.consumerDependencyMap = new Map();
|
|
178
|
+
/**
|
|
179
|
+
* @private
|
|
180
|
+
*
|
|
181
|
+
* Coalesced full-form re-render. Multiple triggers within the same
|
|
182
|
+
* microtask drain into a single `rerenderForm()` call. The set of
|
|
183
|
+
* changed field ids is accumulated across the tick so `rerenderForm`
|
|
184
|
+
* can decide which preserved subtrees must be invalidated (because one
|
|
185
|
+
* of the changed fields is in their own dependency set).
|
|
186
|
+
*/
|
|
187
|
+
_this.pendingChangedFieldIds = new Set();
|
|
188
|
+
return _this;
|
|
189
|
+
}
|
|
190
|
+
FormRenderer_1 = FormRenderer;
|
|
191
|
+
/**
|
|
192
|
+
* Returns the EJ2 component module name.
|
|
193
|
+
* @private
|
|
194
|
+
*/
|
|
195
|
+
FormRenderer.prototype.getModuleName = function () {
|
|
196
|
+
return 'form-renderer';
|
|
197
|
+
};
|
|
198
|
+
/**
|
|
199
|
+
* Return the persisted-state payload. The base class declares this
|
|
200
|
+
* abstract; FormRenderer does not implement state persistence, so an
|
|
201
|
+
* empty object is returned (mirrors the EJ2 convention for
|
|
202
|
+
* non-persistent components).
|
|
203
|
+
* @private
|
|
204
|
+
*/
|
|
205
|
+
FormRenderer.prototype.getPersistData = function () {
|
|
206
|
+
return this.addOnPersist(['formState', JSON.stringify(this.formState)]);
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* Pre-render wiring: enable ripple, init the expression engine + bridge.
|
|
210
|
+
* @private
|
|
211
|
+
*/
|
|
212
|
+
FormRenderer.prototype.preRender = function () {
|
|
213
|
+
enableRipple(true);
|
|
214
|
+
this.createdFired = false;
|
|
215
|
+
this.expressionEngine = new ExpressionEngine();
|
|
216
|
+
};
|
|
217
|
+
/**
|
|
218
|
+
* Main render: normalize schema, build rules + initial values, render tree.
|
|
219
|
+
* @private
|
|
220
|
+
*/
|
|
221
|
+
FormRenderer.prototype.render = function () {
|
|
222
|
+
var _this = this;
|
|
223
|
+
this.clearEngineCaches();
|
|
224
|
+
this.removePreviousRoot();
|
|
225
|
+
this.normalizeSchema();
|
|
226
|
+
this.element.classList.add('form-renderer-container');
|
|
227
|
+
if (this.className) {
|
|
228
|
+
addClass([this.element], this.className.replace(/\s+/g, ' ').trim().split(' '));
|
|
229
|
+
}
|
|
230
|
+
if (this.enableRtl) {
|
|
231
|
+
this.element.classList.add('e-rtl');
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
this.element.classList.remove('e-rtl');
|
|
235
|
+
}
|
|
236
|
+
this.hasLayoutComponentsCache = this.computeHasLayoutComponents();
|
|
237
|
+
this.idToLabelCache = this.computeIdToLabel();
|
|
238
|
+
this.initialValuesCache = this.buildInitialValues();
|
|
239
|
+
this.buildValidationRulesAndBridge();
|
|
240
|
+
// Seed initial values into the form-state values map once.
|
|
241
|
+
if (!this.isInitialized) {
|
|
242
|
+
this.isInitialized = true;
|
|
243
|
+
}
|
|
244
|
+
if (this.formState && this.initialValuesCache) {
|
|
245
|
+
var initKeys = Object.keys(this.initialValuesCache);
|
|
246
|
+
for (var _i = 0, initKeys_1 = initKeys; _i < initKeys_1.length; _i++) {
|
|
247
|
+
var id = initKeys_1[_i];
|
|
248
|
+
var value = this.initialValuesCache[id];
|
|
249
|
+
if (!(this.formState).values[id]) {
|
|
250
|
+
(this.formState).values[id] = value;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
this.currentFormValues = __assign({}, (this.formState ? this.formState.values : {}));
|
|
255
|
+
this.initExpressionEngine();
|
|
256
|
+
this.buildConditionalDependencyMap();
|
|
257
|
+
var root = createElement('form', {
|
|
258
|
+
className: "form-renderer " + this.layoutClass,
|
|
259
|
+
attrs: { role: 'region', 'aria-label': this.processedSchemaCache.settings ? (this.processedSchemaCache.settings.name || 'Form') : 'Form' }
|
|
260
|
+
});
|
|
261
|
+
this.element.appendChild(root);
|
|
262
|
+
this.inputRefs = {};
|
|
263
|
+
(this.processedSchemaCache.components || []).forEach(function (component) {
|
|
264
|
+
var rendered = _this.renderComponent(component);
|
|
265
|
+
if (rendered) {
|
|
266
|
+
root.appendChild(rendered);
|
|
267
|
+
}
|
|
268
|
+
if (component.type === 'richTextEditor') {
|
|
269
|
+
var rteElement = rendered.querySelector('.e-richtexteditor');
|
|
270
|
+
var rteInstance = getComponent(rteElement, 'richtexteditor');
|
|
271
|
+
rteInstance.refreshUI();
|
|
272
|
+
}
|
|
273
|
+
if (component.type === 'dataGrid') {
|
|
274
|
+
var toolbarElement = rendered.querySelector('.e-control.e-toolbar');
|
|
275
|
+
var rteInstance = getComponent(toolbarElement, 'toolbar');
|
|
276
|
+
rteInstance.refresh();
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
if (this.created && !this.createdFired) {
|
|
280
|
+
setTimeout(function () {
|
|
281
|
+
if (Object.keys(_this.inputRefs).length > 0) {
|
|
282
|
+
_this.createdFired = true;
|
|
283
|
+
_this.trigger('created', {});
|
|
284
|
+
}
|
|
285
|
+
}, 0);
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
/**
|
|
289
|
+
* @private
|
|
290
|
+
*/
|
|
291
|
+
FormRenderer.prototype.normalizeSchema = function () {
|
|
292
|
+
var schema = this.schema;
|
|
293
|
+
try {
|
|
294
|
+
if (!schema) {
|
|
295
|
+
this.processedSchemaCache = { version: '1.0.0', components: [] };
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
var parsedInput = schema;
|
|
299
|
+
if (typeof schema === 'string') {
|
|
300
|
+
try {
|
|
301
|
+
parsedInput = JSON.parse(schema);
|
|
302
|
+
}
|
|
303
|
+
catch (err) {
|
|
304
|
+
if (this.failure) {
|
|
305
|
+
this.trigger('failure', {
|
|
306
|
+
error: err
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
this.processedSchemaCache = { version: '1.0.0', components: [] };
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
var isUnifiedSchema = parsedInput && typeof parsedInput === 'object' &&
|
|
314
|
+
parsedInput.properties && Array.isArray(parsedInput.layout);
|
|
315
|
+
if (isUnifiedSchema) {
|
|
316
|
+
this.processedSchemaCache = mergeSchema(parsedInput);
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
var parser = new UniversalJsonParser();
|
|
320
|
+
var parseResult = parser.parse(typeof schema === 'string' ? schema : JSON.stringify(schema));
|
|
321
|
+
if (parseResult.success && parseResult.schema) {
|
|
322
|
+
this.processedSchemaCache = parseResult.schema;
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
if (this.failure) {
|
|
326
|
+
this.trigger('failure', {
|
|
327
|
+
error: parseResult.errors
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
this.processedSchemaCache = { version: '1.0.0', components: [] };
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
catch (error) {
|
|
335
|
+
if (this.failure) {
|
|
336
|
+
this.trigger('failure', {
|
|
337
|
+
error: error
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
this.processedSchemaCache = { version: '1.0.0', components: [] };
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
// ----------------------------------------------------------------------
|
|
344
|
+
// G3 — layout detection + suppression map
|
|
345
|
+
// ----------------------------------------------------------------------
|
|
346
|
+
/**
|
|
347
|
+
* Recursively scan the schema for any `table`/`tabs`/`panel`/`card`
|
|
348
|
+
* (including inside table cells and tab content). Mirrors `hasLayoutComponents`.
|
|
349
|
+
* @private
|
|
350
|
+
*/
|
|
351
|
+
FormRenderer.prototype.computeHasLayoutComponents = function () {
|
|
352
|
+
if (!this.processedSchemaCache.components) {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
var checkForLayoutTypes = function (components) {
|
|
356
|
+
for (var _i = 0, components_1 = components; _i < components_1.length; _i++) {
|
|
357
|
+
var comp = components_1[_i];
|
|
358
|
+
if (['table', 'tabs', 'panel', 'card'].indexOf(comp.type) !== -1) {
|
|
359
|
+
return true;
|
|
360
|
+
}
|
|
361
|
+
if (comp.children && checkForLayoutTypes(comp.children)) {
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
if (comp.tableCells) {
|
|
365
|
+
for (var _a = 0, _b = comp.tableCells; _a < _b.length; _a++) {
|
|
366
|
+
var row = _b[_a];
|
|
367
|
+
for (var _c = 0, row_1 = row; _c < row_1.length; _c++) {
|
|
368
|
+
var cell = row_1[_c];
|
|
369
|
+
if (checkForLayoutTypes(cell)) {
|
|
370
|
+
return true;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (comp.tabItems) {
|
|
376
|
+
for (var _d = 0, _e = comp.tabItems; _d < _e.length; _d++) {
|
|
377
|
+
var tab = _e[_d];
|
|
378
|
+
if (tab.content && checkForLayoutTypes(tab.content)) {
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return false;
|
|
385
|
+
};
|
|
386
|
+
return checkForLayoutTypes(this.processedSchemaCache.components);
|
|
387
|
+
};
|
|
388
|
+
Object.defineProperty(FormRenderer.prototype, "layoutClass", {
|
|
389
|
+
/**
|
|
390
|
+
* The layout CSS class (suppressed when layout components are present).
|
|
391
|
+
* @private
|
|
392
|
+
*/
|
|
393
|
+
get: function () {
|
|
394
|
+
if (this.hasLayoutComponentsCache) {
|
|
395
|
+
return '';
|
|
396
|
+
}
|
|
397
|
+
switch (this.layout) {
|
|
398
|
+
case 'TwoColumn':
|
|
399
|
+
return 'twolayout';
|
|
400
|
+
case 'ThreeColumn':
|
|
401
|
+
return 'threelayout';
|
|
402
|
+
case 'FourColumn':
|
|
403
|
+
return 'fourlayout';
|
|
404
|
+
case 'SingleColumn':
|
|
405
|
+
default:
|
|
406
|
+
return '';
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
enumerable: true,
|
|
410
|
+
configurable: true
|
|
411
|
+
});
|
|
412
|
+
// ----------------------------------------------------------------------
|
|
413
|
+
// G4 — collectAllComponents + idToLabel
|
|
414
|
+
// ----------------------------------------------------------------------
|
|
415
|
+
/**
|
|
416
|
+
* Recursively collect all components including nested ones.
|
|
417
|
+
* @private
|
|
418
|
+
*/
|
|
419
|
+
FormRenderer.prototype.collectAllComponents = function (components) {
|
|
420
|
+
var all = [];
|
|
421
|
+
var traverse = function (comps) {
|
|
422
|
+
comps.forEach(function (comp) {
|
|
423
|
+
all.push(comp);
|
|
424
|
+
if (comp.children && comp.children.length > 0) {
|
|
425
|
+
traverse(comp.children);
|
|
426
|
+
}
|
|
427
|
+
if (comp.tableCells) {
|
|
428
|
+
comp.tableCells.forEach(function (row) {
|
|
429
|
+
row.forEach(function (cell) { return traverse(cell); });
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
if (comp.tabItems) {
|
|
433
|
+
comp.tabItems.forEach(function (tab) {
|
|
434
|
+
if (tab.content) {
|
|
435
|
+
traverse(tab.content);
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
};
|
|
441
|
+
traverse(components);
|
|
442
|
+
return all;
|
|
443
|
+
};
|
|
444
|
+
/**
|
|
445
|
+
* @private
|
|
446
|
+
*/
|
|
447
|
+
FormRenderer.prototype.computeIdToLabel = function () {
|
|
448
|
+
var map = {};
|
|
449
|
+
var traverse = function (comps) {
|
|
450
|
+
comps.forEach(function (comp) {
|
|
451
|
+
if (comp.label && ['panel', 'table', 'tabs', 'card', 'button', 'message', 'splitButton'].indexOf(comp.type) === -1) {
|
|
452
|
+
if (comp.name) {
|
|
453
|
+
map[comp.id] = comp.name;
|
|
454
|
+
}
|
|
455
|
+
else if (comp.label) {
|
|
456
|
+
map[comp.id] = comp.label;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
if (comp.children) {
|
|
460
|
+
traverse(comp.children);
|
|
461
|
+
}
|
|
462
|
+
if (comp.tableCells) {
|
|
463
|
+
comp.tableCells.forEach(function (row) { return row.forEach(function (cell) { return traverse(cell); }); });
|
|
464
|
+
}
|
|
465
|
+
if (comp.tabItems) {
|
|
466
|
+
comp.tabItems.forEach(function (tab) { if (tab.content) {
|
|
467
|
+
traverse(tab.content);
|
|
468
|
+
} });
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
};
|
|
472
|
+
traverse(this.processedSchemaCache.components);
|
|
473
|
+
return map;
|
|
474
|
+
};
|
|
475
|
+
// ----------------------------------------------------------------------
|
|
476
|
+
// G5 — buildValidationRules via FormStateBridge + ConditionalRuleEngine +
|
|
477
|
+
// EMAIL/URL auto-regex + customValidator closure
|
|
478
|
+
// ----------------------------------------------------------------------
|
|
479
|
+
/**
|
|
480
|
+
* @private
|
|
481
|
+
*/
|
|
482
|
+
FormRenderer.prototype.buildValidationRulesAndBridge = function () {
|
|
483
|
+
var _this = this;
|
|
484
|
+
var rules = {};
|
|
485
|
+
var formValues = (this.formState && this.formState.values) || {};
|
|
486
|
+
var collectRules = function (components, parentVisible) {
|
|
487
|
+
if (parentVisible === void 0) { parentVisible = true; }
|
|
488
|
+
components.forEach(function (component) {
|
|
489
|
+
if (!component) {
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
var componentRules = {};
|
|
493
|
+
var isVisible = parentVisible && (component.visible === false
|
|
494
|
+
? false
|
|
495
|
+
: ConditionalRuleEngine.isFieldVisible(component.conditions, formValues));
|
|
496
|
+
var isHidden = parentVisible && ConditionalRuleEngine.isFieldHidden(component.conditions, formValues);
|
|
497
|
+
if (!isVisible || isHidden) {
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
var conditionalRequired = ConditionalRuleEngine.isFieldRequired(component.conditions, formValues);
|
|
501
|
+
var conditionalDisabled = ConditionalRuleEngine.isFieldDisabled(component.conditions, formValues);
|
|
502
|
+
var conditionalReadOnly = ConditionalRuleEngine.isFieldReadOnly(component.conditions, formValues);
|
|
503
|
+
var finalRequired = !!component.required || conditionalRequired;
|
|
504
|
+
var finalDisabled = !!component.disabled || conditionalDisabled;
|
|
505
|
+
var finalReadOnly = !!component.readOnly || conditionalReadOnly;
|
|
506
|
+
if (component.customValidation &&
|
|
507
|
+
Array.isArray(component.customValidation) &&
|
|
508
|
+
component.customValidation.length > 0 &&
|
|
509
|
+
!finalDisabled && !finalReadOnly) {
|
|
510
|
+
var customValidationExpression = component.customValidation && component.customValidation[0] && component.customValidation[0].expression;
|
|
511
|
+
var validationMessage = customValidationExpression && customValidationExpression.match(/\?\s*true\s*:\s*['"](.+?)['"]/) ? customValidationExpression.match(/\?\s*true\s*:\s*['"](.+?)['"]/)[1] : undefined;
|
|
512
|
+
var customMessage = validationMessage ||
|
|
513
|
+
component.validationMessage ||
|
|
514
|
+
(component.label || component.id) + " is invalid";
|
|
515
|
+
var self_1 = _this;
|
|
516
|
+
componentRules.customValidation = [
|
|
517
|
+
function (args) {
|
|
518
|
+
var fieldValue = args.value;
|
|
519
|
+
if (finalRequired && (fieldValue === '' || fieldValue === null || fieldValue === undefined)) {
|
|
520
|
+
return true;
|
|
521
|
+
}
|
|
522
|
+
var idToNameMap = {};
|
|
523
|
+
var allComponents = self_1.collectAllComponents(self_1.processedSchemaCache.components);
|
|
524
|
+
allComponents.forEach(function (comp) {
|
|
525
|
+
if (comp.name) {
|
|
526
|
+
idToNameMap[comp.id] = comp.name;
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
var currentFormValues = __assign({}, (self_1.currentFormValues || {}));
|
|
530
|
+
var cfvKeys = Object.keys(__assign({}, currentFormValues));
|
|
531
|
+
for (var _i = 0, cfvKeys_1 = cfvKeys; _i < cfvKeys_1.length; _i++) {
|
|
532
|
+
var componentId = cfvKeys_1[_i];
|
|
533
|
+
var value = currentFormValues[componentId];
|
|
534
|
+
var fieldName = idToNameMap[componentId];
|
|
535
|
+
if (fieldName) {
|
|
536
|
+
currentFormValues[fieldName] = value;
|
|
537
|
+
delete currentFormValues[componentId];
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
for (var _a = 0, _b = component.customValidation; _a < _b.length; _a++) {
|
|
541
|
+
var rule = _b[_a];
|
|
542
|
+
if (!self_1.customValidationEngine) {
|
|
543
|
+
return true;
|
|
544
|
+
}
|
|
545
|
+
var result = self_1.customValidationEngine.validate(rule.expression, fieldValue, currentFormValues);
|
|
546
|
+
if (result !== true) {
|
|
547
|
+
return false;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
return true;
|
|
551
|
+
},
|
|
552
|
+
customMessage
|
|
553
|
+
];
|
|
554
|
+
}
|
|
555
|
+
if (finalRequired && !finalDisabled && !finalReadOnly) {
|
|
556
|
+
componentRules.required = [true, component.validationMessage || component.label + " is required"];
|
|
557
|
+
}
|
|
558
|
+
if (component.minLength && !finalDisabled && !finalReadOnly) {
|
|
559
|
+
componentRules.minLength = [component.minLength, "Minimum " + component.minLength + " characters allowed"];
|
|
560
|
+
}
|
|
561
|
+
if (component.maxLength && !finalDisabled && !finalReadOnly) {
|
|
562
|
+
componentRules.maxLength = [component.maxLength, "Maximum " + component.maxLength + " characters allowed"];
|
|
563
|
+
}
|
|
564
|
+
if (component.type === 'textbox' && component.textboxType && !component.regex &&
|
|
565
|
+
!componentRules.regex && !finalDisabled && !finalReadOnly) {
|
|
566
|
+
if (component.textboxType === 'email') {
|
|
567
|
+
componentRules.regex = [VALIDATION_REGEX['EMAIL'], component.validationMessage || 'Invalid format'];
|
|
568
|
+
}
|
|
569
|
+
else if (component.textboxType === 'url') {
|
|
570
|
+
componentRules.regex = [VALIDATION_REGEX['URL'], component.validationMessage || 'Invalid format'];
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
if (component.regex && !finalDisabled && !finalReadOnly) {
|
|
574
|
+
componentRules.regex = [component.regex, component.validationMessage || 'Invalid format'];
|
|
575
|
+
}
|
|
576
|
+
if (Object.keys(componentRules).length > 0) {
|
|
577
|
+
rules[component.id] = componentRules;
|
|
578
|
+
}
|
|
579
|
+
if (component.children) {
|
|
580
|
+
collectRules(component.children, isVisible && !isHidden);
|
|
581
|
+
}
|
|
582
|
+
if (component.tableCells) {
|
|
583
|
+
component.tableCells.forEach(function (row) { return row.forEach(function (cell) { return collectRules(cell, isVisible && !isHidden); }); });
|
|
584
|
+
}
|
|
585
|
+
if (component.tabItems) {
|
|
586
|
+
component.tabItems.forEach(function (tab) {
|
|
587
|
+
if (tab.content) {
|
|
588
|
+
collectRules(tab.content, isVisible && !isHidden);
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
};
|
|
594
|
+
collectRules(this.processedSchemaCache.components);
|
|
595
|
+
// Preserve previously-entered values across a re-render.
|
|
596
|
+
var previousValues = this.formState ? __assign({}, this.formState.values) : __assign({}, this.initialValuesCache);
|
|
597
|
+
this.formState = {
|
|
598
|
+
values: Object.assign(Object.create(null), this.initialValuesCache, previousValues),
|
|
599
|
+
errors: this.enablePersistence ? this.formState.errors : Object.create(null)
|
|
600
|
+
};
|
|
601
|
+
// Restore any previously-entered values that the merge above pruned.
|
|
602
|
+
if (previousValues) {
|
|
603
|
+
Object.keys(previousValues).forEach(function (id) {
|
|
604
|
+
if (!(id in _this.formState.values)) {
|
|
605
|
+
_this.formState.values[id] = previousValues[id];
|
|
606
|
+
}
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
// (Re)create the EJ2 native FormValidator bound to the host element.
|
|
610
|
+
if (this.validator) {
|
|
611
|
+
try {
|
|
612
|
+
this.validator.refresh();
|
|
613
|
+
this.validator.destroy();
|
|
614
|
+
}
|
|
615
|
+
catch (e) { /* swallow */ }
|
|
616
|
+
this.validator = null;
|
|
617
|
+
}
|
|
618
|
+
var self = this;
|
|
619
|
+
this.validator = new FormValidator(this.element, {
|
|
620
|
+
rules: rules,
|
|
621
|
+
locale: this.locale,
|
|
622
|
+
ignore: 'e-hidden',
|
|
623
|
+
errorClass: 'e-error',
|
|
624
|
+
validClass: 'e-valid',
|
|
625
|
+
errorElement: 'div',
|
|
626
|
+
errorContainer: 'div',
|
|
627
|
+
customPlacement: function (inputElement, errorElement) {
|
|
628
|
+
// Locate the per-field error-message slot rendered in
|
|
629
|
+
// `renderField` by its id (`error-<component.id>`). The
|
|
630
|
+
// input's `name` attribute matches `component.id`
|
|
631
|
+
// (set in renderField), so `getElementById('error-' + name)`
|
|
632
|
+
// resolves the slot and the validator's error element is
|
|
633
|
+
// appended into it (FormValidator sets
|
|
634
|
+
// `errorElement.style.display = 'block'` before invoking
|
|
635
|
+
// customPlacement, which makes the slot visible).
|
|
636
|
+
var name = inputElement.getAttribute('name');
|
|
637
|
+
if (!name) {
|
|
638
|
+
if (inputElement.parentElement) {
|
|
639
|
+
inputElement.parentElement.appendChild(errorElement);
|
|
640
|
+
}
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
var slot = document.getElementById("error-" + name);
|
|
644
|
+
if (slot) {
|
|
645
|
+
while (slot.firstChild) {
|
|
646
|
+
slot.removeChild(slot.firstChild);
|
|
647
|
+
}
|
|
648
|
+
slot.appendChild(errorElement);
|
|
649
|
+
slot.style.display = 'block';
|
|
650
|
+
}
|
|
651
|
+
else if (inputElement.parentElement) {
|
|
652
|
+
inputElement.parentElement.appendChild(errorElement);
|
|
653
|
+
}
|
|
654
|
+
},
|
|
655
|
+
validationComplete: function (args) {
|
|
656
|
+
// Mirror the validator's error state into formState.errors so
|
|
657
|
+
// submit-time isValid and renderField's initial-paint logic
|
|
658
|
+
// (which read formState.errors[id]) continue to work.
|
|
659
|
+
self.syncFormStateErrors();
|
|
660
|
+
// Keep the per-field error slot's visibility in sync with
|
|
661
|
+
// the validator's outcome. On `success`, the FormValidator's
|
|
662
|
+
// `hideMessage` sets its own error element's `display: none`,
|
|
663
|
+
// but our slot (`error-<id>`) would otherwise keep the stale
|
|
664
|
+
// error element appended. Clear + hide the slot on success.
|
|
665
|
+
if (args && args.inputName) {
|
|
666
|
+
var slot = document.getElementById("error-" + args.inputName);
|
|
667
|
+
if (slot) {
|
|
668
|
+
if (args.status === 'success') {
|
|
669
|
+
while (slot.firstChild) {
|
|
670
|
+
slot.removeChild(slot.firstChild);
|
|
671
|
+
}
|
|
672
|
+
slot.style.display = 'none';
|
|
673
|
+
}
|
|
674
|
+
else if (args.status === 'failure') {
|
|
675
|
+
slot.style.display = 'block';
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
},
|
|
680
|
+
submit: function (event) {
|
|
681
|
+
if (!self.validator) {
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
var isValid = self.validator.validate();
|
|
685
|
+
if (!isValid) {
|
|
686
|
+
if (event && typeof event.preventDefault === 'function') {
|
|
687
|
+
event.preventDefault();
|
|
688
|
+
}
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
self.handleSubmitData(__assign({}, self.formState.values));
|
|
692
|
+
if (event && typeof event.preventDefault === 'function') {
|
|
693
|
+
event.preventDefault();
|
|
694
|
+
}
|
|
695
|
+
},
|
|
696
|
+
reset: function () {
|
|
697
|
+
}
|
|
698
|
+
});
|
|
699
|
+
// Apply cached previous values to the FormValidator's input elements.
|
|
700
|
+
// The validator reads live values from inputs at validate() time, but
|
|
701
|
+
// initial values seeded here ensure any pre-populated values are
|
|
702
|
+
// respected even if a field is never focused before submit.
|
|
703
|
+
if (previousValues) {
|
|
704
|
+
Object.keys(previousValues).forEach(function (id) {
|
|
705
|
+
var inputEl = _this.validator.getInputElement(id);
|
|
706
|
+
if (inputEl && previousValues[id] !== undefined && previousValues[id] !== null) {
|
|
707
|
+
try {
|
|
708
|
+
inputEl.value = String(previousValues[id]);
|
|
709
|
+
}
|
|
710
|
+
catch (e) { /* swallow */ }
|
|
711
|
+
}
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
};
|
|
715
|
+
/**
|
|
716
|
+
* @private
|
|
717
|
+
*
|
|
718
|
+
* Mirror the FormValidator's `errorRules` array into `this.formState.errors`
|
|
719
|
+
* keyed by field `name` (= `component.id`). Called after every
|
|
720
|
+
* `validationComplete` callback and from submit-time `isValid` reads.
|
|
721
|
+
*/
|
|
722
|
+
FormRenderer.prototype.syncFormStateErrors = function () {
|
|
723
|
+
var errors = {};
|
|
724
|
+
if (this.validator) {
|
|
725
|
+
var errorRules = this.validator.errorRules || [];
|
|
726
|
+
for (var _i = 0, errorRules_1 = errorRules; _i < errorRules_1.length; _i++) {
|
|
727
|
+
var rule = errorRules_1[_i];
|
|
728
|
+
if (rule && rule.name) {
|
|
729
|
+
errors[rule.name] = rule.message;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
this.formState.errors = errors;
|
|
734
|
+
};
|
|
735
|
+
/**
|
|
736
|
+
* @private
|
|
737
|
+
*/
|
|
738
|
+
FormRenderer.prototype.buildInitialValues = function () {
|
|
739
|
+
var _this = this;
|
|
740
|
+
var values = {};
|
|
741
|
+
var allComponents = this.collectAllComponents(this.processedSchemaCache.components);
|
|
742
|
+
var componentIds = new Set();
|
|
743
|
+
allComponents.forEach(function (comp) {
|
|
744
|
+
if (comp.type === 'staticHtml') {
|
|
745
|
+
componentIds.add(comp.id);
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
if (comp.type === 'dataGrid') {
|
|
749
|
+
values[comp.id] = Array.isArray(comp.dataSource)
|
|
750
|
+
? comp.dataSource
|
|
751
|
+
: [];
|
|
752
|
+
}
|
|
753
|
+
if (comp.defaultValue !== undefined) {
|
|
754
|
+
values[comp.id] = comp.defaultValue;
|
|
755
|
+
}
|
|
756
|
+
else {
|
|
757
|
+
switch (comp.type) {
|
|
758
|
+
case 'checkbox':
|
|
759
|
+
case 'switch':
|
|
760
|
+
values[comp.id] = false;
|
|
761
|
+
break;
|
|
762
|
+
default:
|
|
763
|
+
break;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
componentIds.add(comp.id);
|
|
767
|
+
});
|
|
768
|
+
if (this.formState) {
|
|
769
|
+
Object.keys(this.formState.values)
|
|
770
|
+
.filter(function (key) { return !componentIds.has(key); })
|
|
771
|
+
.forEach(function (key) {
|
|
772
|
+
delete (_this.formState).values[key];
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
return values;
|
|
776
|
+
};
|
|
777
|
+
// ----------------------------------------------------------------------
|
|
778
|
+
// Expression engine init (used by G9)
|
|
779
|
+
// ----------------------------------------------------------------------
|
|
780
|
+
/**
|
|
781
|
+
* Initialize the expression engine dependency graph + computed-field set.
|
|
782
|
+
* @private
|
|
783
|
+
*/
|
|
784
|
+
FormRenderer.prototype.initExpressionEngine = function () {
|
|
785
|
+
if (!this.processedSchemaCache.components || this.processedSchemaCache.components.length === 0) {
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
try {
|
|
789
|
+
var engine = this.expressionEngine;
|
|
790
|
+
var allComponents = this.collectAllComponents(this.processedSchemaCache.components);
|
|
791
|
+
var graph = engine.buildDependencyGraph(allComponents);
|
|
792
|
+
var cycles = engine.detectCircularDependencies(graph);
|
|
793
|
+
if (cycles.length > 0) {
|
|
794
|
+
}
|
|
795
|
+
this.expressionDependencyGraph = graph;
|
|
796
|
+
var computedFields_1 = new Set();
|
|
797
|
+
allComponents.forEach(function (comp) {
|
|
798
|
+
if (comp.expressionValue) {
|
|
799
|
+
computedFields_1.add(comp.id);
|
|
800
|
+
}
|
|
801
|
+
});
|
|
802
|
+
this.computedFieldNames = computedFields_1;
|
|
803
|
+
}
|
|
804
|
+
catch (error) {
|
|
805
|
+
if (this.failure) {
|
|
806
|
+
this.trigger('failure', {
|
|
807
|
+
error: error
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
/**
|
|
813
|
+
* @private
|
|
814
|
+
*/
|
|
815
|
+
FormRenderer.prototype.disposeTooltipInstance = function (fieldId) {
|
|
816
|
+
var existingTooltip = this.tooltipRefs[fieldId] || null;
|
|
817
|
+
if (existingTooltip) {
|
|
818
|
+
try {
|
|
819
|
+
existingTooltip.destroy();
|
|
820
|
+
}
|
|
821
|
+
catch (e) {
|
|
822
|
+
this.trigger('failure', {
|
|
823
|
+
error: e
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
this.renderedInstances = this.renderedInstances.filter(function (inst) { return inst !== existingTooltip; });
|
|
827
|
+
this.tooltipRefs[fieldId] = null;
|
|
828
|
+
}
|
|
829
|
+
};
|
|
830
|
+
FormRenderer.prototype.renderField = function (component) {
|
|
831
|
+
var _this = this;
|
|
832
|
+
var formState = this.formState;
|
|
833
|
+
var formValues = formState ? formState.values : {};
|
|
834
|
+
if (['panel', 'table', 'tabs', 'card', 'message', 'button', 'splitButton', 'staticHtml'].indexOf(component.type) !== -1) {
|
|
835
|
+
return null;
|
|
836
|
+
}
|
|
837
|
+
var conditionalReadOnly = ConditionalRuleEngine.isFieldReadOnly(component.conditions, formValues);
|
|
838
|
+
var conditionalDisabled = ConditionalRuleEngine.isFieldDisabled(component.conditions, formValues);
|
|
839
|
+
var conditionalRequired = ConditionalRuleEngine.isFieldRequired(component.conditions, formValues);
|
|
840
|
+
var conditionalValue = ConditionalRuleEngine.getConditionalValue(component.conditions, formValues);
|
|
841
|
+
var computedReadOnly = !!component.readOnly || conditionalReadOnly;
|
|
842
|
+
var computedDisabled = !!component.disabled || conditionalDisabled;
|
|
843
|
+
var computedRequired = !!component.required || conditionalRequired;
|
|
844
|
+
var minMaxConstraints = MinMaxRuleEngine.getConstraints(component.minMaxRange, formValues, component.type);
|
|
845
|
+
if (component.type === 'dateTime') {
|
|
846
|
+
var dateConstraints = MinMaxRuleEngine.getConstraints(component.minMaxRange, formValues, 'date');
|
|
847
|
+
var timeConstraints = MinMaxRuleEngine.getConstraints(component.minMaxTimeRange, formValues, 'time');
|
|
848
|
+
minMaxConstraints = {
|
|
849
|
+
minValue: undefined,
|
|
850
|
+
maxValue: undefined,
|
|
851
|
+
minDate: dateConstraints.minDate,
|
|
852
|
+
maxDate: dateConstraints.maxDate,
|
|
853
|
+
minTime: timeConstraints.minTime,
|
|
854
|
+
maxTime: timeConstraints.maxTime
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
var labelPos = component.labelPosition || 'top';
|
|
858
|
+
var labelWidth = component.labelWidth || 'auto';
|
|
859
|
+
var showLabel = !component.hideLabel && !!component.label &&
|
|
860
|
+
['checkbox', 'button', 'staticHtml'].indexOf(component.type) === -1;
|
|
861
|
+
var shouldShowError = !!(formState && formState.errors[component.id]);
|
|
862
|
+
var isRequiredError = shouldShowError && !!(formState && formState.errors[component.id].indexOf('required') !== -1);
|
|
863
|
+
var hasError = shouldShowError && !(isRequiredError && !computedRequired);
|
|
864
|
+
// setValueWhen false→true transition tracking.
|
|
865
|
+
var conditionIsActive = conditionalValue !== undefined;
|
|
866
|
+
var wasConditionActive = this.conditionalValueApplied[component.id] || false;
|
|
867
|
+
if (formState) {
|
|
868
|
+
if (conditionIsActive && !wasConditionActive) {
|
|
869
|
+
formState.values[component.id] = conditionalValue;
|
|
870
|
+
this.conditionalValueApplied[component.id] = true;
|
|
871
|
+
}
|
|
872
|
+
else if (!conditionIsActive && wasConditionActive) {
|
|
873
|
+
this.conditionalValueApplied[component.id] = false;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
var formGroup = createElement('div', {
|
|
877
|
+
className: 'form-group',
|
|
878
|
+
attrs: { 'data-field-id': component.id, 'data-fr-field': component.id }
|
|
879
|
+
});
|
|
880
|
+
var fieldWrapper = createElement('div', {
|
|
881
|
+
className: "field-wrapper label-" + labelPos.toLowerCase()
|
|
882
|
+
});
|
|
883
|
+
if (showLabel) {
|
|
884
|
+
var labelEl = createElement('label', {
|
|
885
|
+
className: 'field-label'
|
|
886
|
+
});
|
|
887
|
+
if (inputTargetTypes.indexOf(component.type) !== -1 || component.type === 'textarea') {
|
|
888
|
+
labelEl.setAttribute('for', !isNullOrUndefined(this.element.id) ? this.element.id + '-' + component.name : component.name);
|
|
889
|
+
}
|
|
890
|
+
labelEl.style.width = labelPos.toLowerCase() === 'left' ? labelWidth + "%" : 'auto';
|
|
891
|
+
var span = createElement('span');
|
|
892
|
+
span.textContent = component.label;
|
|
893
|
+
labelEl.appendChild(span);
|
|
894
|
+
if (computedRequired) {
|
|
895
|
+
var asterisk = createElement('span', { className: 'required-asterisk' });
|
|
896
|
+
asterisk.textContent = '*';
|
|
897
|
+
labelEl.appendChild(asterisk);
|
|
898
|
+
}
|
|
899
|
+
this.disposeTooltipInstance(component.id);
|
|
900
|
+
if (component.tooltip) {
|
|
901
|
+
var tooltipHost = createElement('span', {
|
|
902
|
+
className: 'e-msg-icon e-icons e-circle-info tooltip-icon',
|
|
903
|
+
attrs: {
|
|
904
|
+
'aria-label': component.tooltip,
|
|
905
|
+
title: component.tooltip,
|
|
906
|
+
tabindex: '0',
|
|
907
|
+
role: 'button',
|
|
908
|
+
'aria-haspopup': 'true'
|
|
909
|
+
}
|
|
910
|
+
});
|
|
911
|
+
tooltipHost.style.cursor = 'pointer';
|
|
912
|
+
tooltipHost.style.display = 'flex';
|
|
913
|
+
tooltipHost.style.alignItems = 'center';
|
|
914
|
+
tooltipHost.style.justifyContent = 'center';
|
|
915
|
+
tooltipHost.style.flexShrink = '0';
|
|
916
|
+
labelEl.appendChild(tooltipHost);
|
|
917
|
+
var tooltip = new Tooltip({ content: component.tooltip, enableRtl: this.enableRtl });
|
|
918
|
+
tooltip.appendTo(tooltipHost);
|
|
919
|
+
this.tooltipRefs[component.id] = tooltip;
|
|
920
|
+
this.renderedInstances.push(tooltip);
|
|
921
|
+
}
|
|
922
|
+
fieldWrapper.appendChild(labelEl);
|
|
923
|
+
}
|
|
924
|
+
var fieldInput = createElement('div', {
|
|
925
|
+
className: 'field-input',
|
|
926
|
+
});
|
|
927
|
+
var isIndexedRef = (component.type === 'radio' || component.type === 'checkboxGroup') && Array.isArray(component.options);
|
|
928
|
+
var setInputRef = isIndexedRef
|
|
929
|
+
? function (el, idx) {
|
|
930
|
+
if (el && typeof idx === 'number') {
|
|
931
|
+
_this.inputRefs[component.name + "_" + idx] = el;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
: function (el) {
|
|
935
|
+
if (el) {
|
|
936
|
+
_this.inputRefs[component.name] = el;
|
|
937
|
+
}
|
|
938
|
+
};
|
|
939
|
+
var mergedComponent = __assign({}, component, { readOnly: computedReadOnly, disabled: computedDisabled, required: computedRequired }, (minMaxConstraints.minValue !== undefined && { minValue: minMaxConstraints.minValue }), (minMaxConstraints.maxValue !== undefined && { maxValue: minMaxConstraints.maxValue }), (minMaxConstraints.minDate !== undefined && { minDate: minMaxConstraints.minDate }), (minMaxConstraints.maxDate !== undefined && { maxDate: minMaxConstraints.maxDate }), (minMaxConstraints.minTime !== undefined && { minTime: minMaxConstraints.minTime }), (minMaxConstraints.maxTime !== undefined && { maxTime: minMaxConstraints.maxTime }));
|
|
940
|
+
var onChangeHandler = function (value) {
|
|
941
|
+
if (component.type !== 'staticHtml') {
|
|
942
|
+
if (component.type === 'signature') {
|
|
943
|
+
var ref = _this.inputRefs[value];
|
|
944
|
+
value = ref.getSignature();
|
|
945
|
+
}
|
|
946
|
+
if (component.type === 'imageEditor') {
|
|
947
|
+
var ref = _this.inputRefs[value];
|
|
948
|
+
var imageData = ref.getImageData();
|
|
949
|
+
var canvas = document.createElement('canvas');
|
|
950
|
+
canvas.width = imageData.width;
|
|
951
|
+
canvas.height = imageData.height;
|
|
952
|
+
var context = canvas.getContext('2d');
|
|
953
|
+
if (context) {
|
|
954
|
+
context.putImageData(imageData, 0, 0);
|
|
955
|
+
}
|
|
956
|
+
value = canvas.toDataURL();
|
|
957
|
+
}
|
|
958
|
+
if (component.type === 'radio' || component.type === 'checkboxGroup') {
|
|
959
|
+
var inputElement = _this.validator.getInputElement(component.id);
|
|
960
|
+
inputElement.value = value;
|
|
961
|
+
}
|
|
962
|
+
value = formatDateValues(component, value);
|
|
963
|
+
if (_this.formState && formState) {
|
|
964
|
+
_this.formState.values[component.id] = value;
|
|
965
|
+
formState.values[component.id] = value;
|
|
966
|
+
}
|
|
967
|
+
_this.trigger('change', { fieldName: component.name, label: component.label || component.id, value: value });
|
|
968
|
+
_this.afterValueChange(component.id);
|
|
969
|
+
}
|
|
970
|
+
};
|
|
971
|
+
var targetTag = inputTargetTypes.indexOf(component.type) !== -1
|
|
972
|
+
? 'input' : component.type === 'textarea' ? 'textarea' : 'div';
|
|
973
|
+
var target = createElement(targetTag, {
|
|
974
|
+
attrs: { 'aria-label': component.label + " " + component.type }
|
|
975
|
+
});
|
|
976
|
+
if (component.type === 'radio' || component.type === 'checkboxGroup') {
|
|
977
|
+
var inputEl = createElement('input');
|
|
978
|
+
inputEl.id = !isNullOrUndefined(this.element.id) ? this.element.id + '-' + component.name : component.name;
|
|
979
|
+
inputEl.setAttribute('name', component.id);
|
|
980
|
+
inputEl.style.display = 'none';
|
|
981
|
+
inputEl.setAttribute('aria-hidden', 'true');
|
|
982
|
+
target.appendChild(inputEl);
|
|
983
|
+
}
|
|
984
|
+
else {
|
|
985
|
+
target.id = !isNullOrUndefined(this.element.id) ? this.element.id + '-' + component.name : component.name;
|
|
986
|
+
target.setAttribute('name', component.id);
|
|
987
|
+
}
|
|
988
|
+
fieldInput.appendChild(target);
|
|
989
|
+
var instance = renderFormField({
|
|
990
|
+
component: mergedComponent,
|
|
991
|
+
value: formState ? formState.values[component.id] : undefined,
|
|
992
|
+
onChange: onChangeHandler,
|
|
993
|
+
formState: formState,
|
|
994
|
+
settings: this.processedSchemaCache.settings,
|
|
995
|
+
onBlur: function () {
|
|
996
|
+
if (formState && component.type !== 'staticHtml') {
|
|
997
|
+
if (_this.validator) {
|
|
998
|
+
try {
|
|
999
|
+
_this.beforeValidate(component.id);
|
|
1000
|
+
_this.validator.validate(component.id);
|
|
1001
|
+
}
|
|
1002
|
+
catch (e) { /* swallow */ }
|
|
1003
|
+
_this.syncFormStateErrors();
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
},
|
|
1007
|
+
ref: setInputRef,
|
|
1008
|
+
hasError: hasError,
|
|
1009
|
+
locale: this.locale,
|
|
1010
|
+
components: this.collectAllComponents(this.processedSchemaCache.components),
|
|
1011
|
+
enableRtl: this.enableRtl,
|
|
1012
|
+
target: target,
|
|
1013
|
+
l10n: this.l10n
|
|
1014
|
+
});
|
|
1015
|
+
if (Array.isArray(instance)) {
|
|
1016
|
+
instance.forEach(function (entry) {
|
|
1017
|
+
if (entry && typeof entry.destroy === 'function') {
|
|
1018
|
+
_this.renderedInstances.push(entry);
|
|
1019
|
+
}
|
|
1020
|
+
else if (entry instanceof HTMLElement) {
|
|
1021
|
+
for (var _i = 0, _a = Object.keys(_this.inputRefs); _i < _a.length; _i++) {
|
|
1022
|
+
var key = _a[_i];
|
|
1023
|
+
var inst = _this.inputRefs[key];
|
|
1024
|
+
var hostEl = inst && (inst.element || inst.target || (inst.hostElement && inst.hostElement()));
|
|
1025
|
+
if (hostEl && (hostEl === entry || entry.contains(hostEl))) {
|
|
1026
|
+
_this.renderedInstances.push(inst);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
else if (instance) {
|
|
1033
|
+
if (typeof instance.destroy === 'function') {
|
|
1034
|
+
this.renderedInstances.push(instance);
|
|
1035
|
+
}
|
|
1036
|
+
else if (instance instanceof HTMLElement) {
|
|
1037
|
+
for (var _i = 0, _a = Object.keys(this.inputRefs); _i < _a.length; _i++) {
|
|
1038
|
+
var key = _a[_i];
|
|
1039
|
+
var inst = this.inputRefs[key];
|
|
1040
|
+
var hostEl = inst && (inst.element || inst.target || (inst.hostElement && inst.hostElement()));
|
|
1041
|
+
if (hostEl && (hostEl === instance || instance.contains(hostEl))) {
|
|
1042
|
+
this.renderedInstances.push(inst);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
var errorSlot = createElement('div', {
|
|
1048
|
+
id: 'error-' + (component.id || component.name || component.label),
|
|
1049
|
+
className: 'e-error-message',
|
|
1050
|
+
attrs: { role: 'alert', 'aria-live': 'assertive' }
|
|
1051
|
+
});
|
|
1052
|
+
errorSlot.style.display = 'none';
|
|
1053
|
+
if (hasError && formState && formState.errors[component.id]) {
|
|
1054
|
+
errorSlot.textContent = formState.errors[component.id];
|
|
1055
|
+
errorSlot.classList.add('e-error');
|
|
1056
|
+
errorSlot.style.display = 'block';
|
|
1057
|
+
}
|
|
1058
|
+
fieldInput.appendChild(errorSlot);
|
|
1059
|
+
fieldWrapper.appendChild(fieldInput);
|
|
1060
|
+
formGroup.appendChild(fieldWrapper);
|
|
1061
|
+
if (component.description) {
|
|
1062
|
+
var desc = createElement('div', {
|
|
1063
|
+
className: 'field-description',
|
|
1064
|
+
attrs: { 'aria-describedby': "description-" + component.label }
|
|
1065
|
+
});
|
|
1066
|
+
desc.textContent = component.description;
|
|
1067
|
+
formGroup.appendChild(desc);
|
|
1068
|
+
}
|
|
1069
|
+
return formGroup;
|
|
1070
|
+
};
|
|
1071
|
+
/**
|
|
1072
|
+
* @private
|
|
1073
|
+
*/
|
|
1074
|
+
FormRenderer.prototype.renderComponent = function (component) {
|
|
1075
|
+
var _this = this;
|
|
1076
|
+
var formState = this.formState;
|
|
1077
|
+
var formValues = formState ? formState.values : {};
|
|
1078
|
+
var isVisible = component.visible === false
|
|
1079
|
+
? false
|
|
1080
|
+
: ConditionalRuleEngine.isFieldVisible(component.conditions, formValues);
|
|
1081
|
+
var isHidden = ConditionalRuleEngine.isFieldHidden(component.conditions, formValues);
|
|
1082
|
+
if (!isVisible || isHidden) {
|
|
1083
|
+
if (formState && formState.values[component.id] !== undefined && formState.values[component.id] !== null) {
|
|
1084
|
+
formState.values[component.id] = null;
|
|
1085
|
+
}
|
|
1086
|
+
return null;
|
|
1087
|
+
}
|
|
1088
|
+
// Re-attach preserved subtree for expensive / remote-data components
|
|
1089
|
+
// (dataGrid / dropdown / multiselect / checkboxGroup / lookup). The
|
|
1090
|
+
// subtree was detached in `rerenderForm` before the rest of the tree
|
|
1091
|
+
// was destroyed; re-attaching it here avoids a re-fetch of remote
|
|
1092
|
+
// data sources and prevents the visible blink of a fresh render.
|
|
1093
|
+
if (FormRenderer_1.PRESERVABLE_TYPES.has(component.type) && this.preservedFields.has(component.id)) {
|
|
1094
|
+
var preservedNode = this.preservedFields.get(component.id);
|
|
1095
|
+
if (preservedNode) {
|
|
1096
|
+
this.preservedFields.delete(component.id);
|
|
1097
|
+
return preservedNode;
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
switch (component.type) {
|
|
1101
|
+
case 'panel': {
|
|
1102
|
+
var fieldset = createElement('fieldset', {
|
|
1103
|
+
className: "form-panel-renderer " + (component.cssClass || '')
|
|
1104
|
+
});
|
|
1105
|
+
fieldset.style.minInlineSize = 'unset';
|
|
1106
|
+
if (component.label && !component.hideLabel) {
|
|
1107
|
+
var legend = createElement('legend', {
|
|
1108
|
+
className: !component.hideBorders ? 'panel-legend' : ''
|
|
1109
|
+
});
|
|
1110
|
+
legend.textContent = component.label;
|
|
1111
|
+
fieldset.appendChild(legend);
|
|
1112
|
+
}
|
|
1113
|
+
var content_1 = createElement('div', { className: 'panel-content' });
|
|
1114
|
+
(component.children || []).forEach(function (child) {
|
|
1115
|
+
var rendered = _this.renderComponent(child);
|
|
1116
|
+
if (rendered) {
|
|
1117
|
+
content_1.appendChild(rendered);
|
|
1118
|
+
}
|
|
1119
|
+
});
|
|
1120
|
+
fieldset.appendChild(content_1);
|
|
1121
|
+
return fieldset;
|
|
1122
|
+
}
|
|
1123
|
+
case 'table': {
|
|
1124
|
+
var tableRows = component.rows || (component.tableCells ? component.tableCells.length : 2);
|
|
1125
|
+
var tableCols = component.columns || (component.tableCells && component.tableCells[0] ? component.tableCells[0].length : 2);
|
|
1126
|
+
var defaultColSpan = Math.floor(12 / tableCols);
|
|
1127
|
+
var columnWidthsProp = component.columnWidths;
|
|
1128
|
+
var splittedWidths = columnWidthsProp ? splitWidths(columnWidthsProp.widths) : [];
|
|
1129
|
+
var tableDiv = createElement('div', {
|
|
1130
|
+
className: "form-table-renderer " + (component.hideBorders ? '' : 'table-border') + " " + (component.cssClass || ''),
|
|
1131
|
+
attrs: { role: 'group', 'aria-label': component.label || 'Table Layout' }
|
|
1132
|
+
});
|
|
1133
|
+
for (var rowIndex = 0; rowIndex < tableRows; rowIndex++) {
|
|
1134
|
+
var rowDiv = createElement('div', { className: 'e-form-table-row' });
|
|
1135
|
+
if (component.hideBorders) {
|
|
1136
|
+
rowDiv.style.display = 'flex';
|
|
1137
|
+
rowDiv.style.gap = '1.5rem';
|
|
1138
|
+
}
|
|
1139
|
+
var _loop_1 = function (colIndex) {
|
|
1140
|
+
var cellComponents = (component.tableCells && component.tableCells[rowIndex] &&
|
|
1141
|
+
component.tableCells[rowIndex][colIndex]) || [];
|
|
1142
|
+
var cellSpan = defaultColSpan;
|
|
1143
|
+
var colWidthValue = splittedWidths[colIndex];
|
|
1144
|
+
var isSingleColumn = tableCols === 1;
|
|
1145
|
+
var cellStyle = isSingleColumn ? { width: '100%' } : (colWidthValue ? { width: colWidthValue + "%" } : {});
|
|
1146
|
+
var allNullWidths = !columnWidthsProp || (Array.isArray(columnWidthsProp.widths) && columnWidthsProp.widths.every(function (w) { return w === null; }));
|
|
1147
|
+
var isFirstRow = rowIndex === 0;
|
|
1148
|
+
var cellDiv = createElement('div', {
|
|
1149
|
+
className: "e-form-table-cell " + (colWidthValue ? '' : allNullWidths ? "col-" + cellSpan : '')
|
|
1150
|
+
});
|
|
1151
|
+
if (component.hideBorders) {
|
|
1152
|
+
Object.assign(cellDiv.style, cellStyle, { padding: isFirstRow ? '0' : '12px 0 0 0' });
|
|
1153
|
+
}
|
|
1154
|
+
else {
|
|
1155
|
+
Object.assign(cellDiv.style, cellStyle);
|
|
1156
|
+
}
|
|
1157
|
+
cellComponents.forEach(function (comp) {
|
|
1158
|
+
var rendered = _this.renderComponent(comp);
|
|
1159
|
+
if (rendered) {
|
|
1160
|
+
cellDiv.appendChild(rendered);
|
|
1161
|
+
}
|
|
1162
|
+
});
|
|
1163
|
+
rowDiv.appendChild(cellDiv);
|
|
1164
|
+
};
|
|
1165
|
+
for (var colIndex = 0; colIndex < tableCols; colIndex++) {
|
|
1166
|
+
_loop_1(colIndex);
|
|
1167
|
+
}
|
|
1168
|
+
tableDiv.appendChild(rowDiv);
|
|
1169
|
+
}
|
|
1170
|
+
return tableDiv;
|
|
1171
|
+
}
|
|
1172
|
+
case 'tabs': {
|
|
1173
|
+
var tabsDiv = createElement('div', {
|
|
1174
|
+
className: "form-tabs-renderer " + (component.cssClass || '')
|
|
1175
|
+
});
|
|
1176
|
+
var tabHost = createElement('div', {
|
|
1177
|
+
className: 'e-tab'
|
|
1178
|
+
});
|
|
1179
|
+
tabHost.id = component.id;
|
|
1180
|
+
var tabHeader_1 = createElement('div', {
|
|
1181
|
+
className: 'e-tab-header'
|
|
1182
|
+
});
|
|
1183
|
+
var tabContent_1 = createElement('div', {
|
|
1184
|
+
className: 'e-content'
|
|
1185
|
+
});
|
|
1186
|
+
tabHost.appendChild(tabHeader_1);
|
|
1187
|
+
tabHost.appendChild(tabContent_1);
|
|
1188
|
+
tabsDiv.appendChild(tabHost);
|
|
1189
|
+
// Build the tab's host DOM up front so each `.e-content > .e-item`
|
|
1190
|
+
// wrapper exists during initialization. Using the JSON `items`
|
|
1191
|
+
// path causes EJ2 to lazily materialize only the active content
|
|
1192
|
+
// panel, which leaves the remaining rendered tab bodies missing
|
|
1193
|
+
// from the live DOM. By seeding the HTML markup structure and
|
|
1194
|
+
// inserting the already-rendered live child nodes into every
|
|
1195
|
+
// content item, the bound EJ2 instances stay attached and all
|
|
1196
|
+
// tabs render correctly.
|
|
1197
|
+
(component.tabItems || []).forEach(function (tab) {
|
|
1198
|
+
var headerItem = createElement('div');
|
|
1199
|
+
headerItem.textContent = tab.header;
|
|
1200
|
+
tabHeader_1.appendChild(headerItem);
|
|
1201
|
+
var contentItem = createElement('div');
|
|
1202
|
+
var contentDiv = createElement('div', { className: 'tab-content-renderer' });
|
|
1203
|
+
(tab.content || []).forEach(function (comp) {
|
|
1204
|
+
var rendered = _this.renderComponent(comp);
|
|
1205
|
+
if (rendered) {
|
|
1206
|
+
contentDiv.appendChild(rendered);
|
|
1207
|
+
}
|
|
1208
|
+
});
|
|
1209
|
+
contentItem.appendChild(contentDiv);
|
|
1210
|
+
tabContent_1.appendChild(contentItem);
|
|
1211
|
+
});
|
|
1212
|
+
var tab = new Tab({
|
|
1213
|
+
enableRtl: this.enableRtl,
|
|
1214
|
+
cssClass: component.cssClass,
|
|
1215
|
+
enablePersistence: true,
|
|
1216
|
+
animation: { previous: { effect: 'None' }, next: { effect: 'None' } },
|
|
1217
|
+
selected: function (args) {
|
|
1218
|
+
if (typeof window === 'undefined')
|
|
1219
|
+
return null;
|
|
1220
|
+
try {
|
|
1221
|
+
window.localStorage.setItem('tab' + component.id, JSON.stringify({
|
|
1222
|
+
selectedItem: args.selectedIndex
|
|
1223
|
+
}));
|
|
1224
|
+
}
|
|
1225
|
+
catch (_a) {
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
});
|
|
1229
|
+
tab.appendTo(tabHost);
|
|
1230
|
+
this.renderedInstances.push(tab);
|
|
1231
|
+
return tabsDiv;
|
|
1232
|
+
}
|
|
1233
|
+
case 'card': {
|
|
1234
|
+
var fieldset = createElement('fieldset', {
|
|
1235
|
+
className: "e-card form-card-renderer " + (component.cssClass || '')
|
|
1236
|
+
});
|
|
1237
|
+
fieldset.style.minInlineSize = 'unset';
|
|
1238
|
+
fieldset.style.padding = '0';
|
|
1239
|
+
var header = createElement('div', { className: 'e-card-header' });
|
|
1240
|
+
var caption = createElement('div', { className: 'e-card-header-caption' });
|
|
1241
|
+
var title = createElement('div', { className: 'e-card-title' });
|
|
1242
|
+
title.textContent = component.cardTitle || component.label || 'Card Title';
|
|
1243
|
+
caption.appendChild(title);
|
|
1244
|
+
if (component.cardSubtitle) {
|
|
1245
|
+
var sub = createElement('div', { className: 'e-card-sub-title' });
|
|
1246
|
+
sub.textContent = component.cardSubtitle;
|
|
1247
|
+
caption.appendChild(sub);
|
|
1248
|
+
}
|
|
1249
|
+
header.appendChild(caption);
|
|
1250
|
+
fieldset.appendChild(header);
|
|
1251
|
+
var content_2 = createElement('div', { className: 'e-card-content card-content' });
|
|
1252
|
+
(component.children || []).forEach(function (child) {
|
|
1253
|
+
var rendered = _this.renderComponent(child);
|
|
1254
|
+
if (rendered) {
|
|
1255
|
+
content_2.appendChild(rendered);
|
|
1256
|
+
}
|
|
1257
|
+
});
|
|
1258
|
+
fieldset.appendChild(content_2);
|
|
1259
|
+
return fieldset;
|
|
1260
|
+
}
|
|
1261
|
+
case 'staticHtml': {
|
|
1262
|
+
var renderer = new StaticHtmlRenderer({
|
|
1263
|
+
content: component.defaultValue || '<p>HTML content</p>',
|
|
1264
|
+
id: component.id,
|
|
1265
|
+
enableHtmlSanitizer: component.enableHtmlSanitizer
|
|
1266
|
+
});
|
|
1267
|
+
var el = renderer.render();
|
|
1268
|
+
this.renderedInstances.push(renderer);
|
|
1269
|
+
return el;
|
|
1270
|
+
}
|
|
1271
|
+
case 'message': {
|
|
1272
|
+
var wrapper = createElement('div', { className: 'form-group' });
|
|
1273
|
+
var target = createElement('div');
|
|
1274
|
+
target.id = component.name;
|
|
1275
|
+
target.setAttribute('name', component.id);
|
|
1276
|
+
wrapper.appendChild(target);
|
|
1277
|
+
var instance = renderFormField({
|
|
1278
|
+
component: component,
|
|
1279
|
+
locale: this.locale,
|
|
1280
|
+
value: undefined,
|
|
1281
|
+
onChange: function () { },
|
|
1282
|
+
enableRtl: this.enableRtl,
|
|
1283
|
+
target: target,
|
|
1284
|
+
ref: function (el, idx) {
|
|
1285
|
+
if (idx !== undefined) {
|
|
1286
|
+
_this.inputRefs[component.name + "_" + idx] = el;
|
|
1287
|
+
}
|
|
1288
|
+
else {
|
|
1289
|
+
_this.inputRefs[component.name] = el;
|
|
1290
|
+
}
|
|
1291
|
+
},
|
|
1292
|
+
});
|
|
1293
|
+
if (Array.isArray(instance)) {
|
|
1294
|
+
instance.forEach(function (entry) {
|
|
1295
|
+
if (entry) {
|
|
1296
|
+
_this.renderedInstances.push(entry);
|
|
1297
|
+
}
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
else if (instance) {
|
|
1301
|
+
this.renderedInstances.push(instance);
|
|
1302
|
+
}
|
|
1303
|
+
return wrapper;
|
|
1304
|
+
}
|
|
1305
|
+
case 'button': {
|
|
1306
|
+
var buttonlDisabled = ConditionalRuleEngine.isFieldDisabled(component.conditions, formValues);
|
|
1307
|
+
var buttonComputed = !!component.disabled || buttonlDisabled;
|
|
1308
|
+
var wrapper = createElement('div', { className: 'form-group' });
|
|
1309
|
+
var target = createElement('div');
|
|
1310
|
+
target.id = component.name;
|
|
1311
|
+
target.setAttribute('name', component.id);
|
|
1312
|
+
wrapper.appendChild(target);
|
|
1313
|
+
var handleButtonClick = function (event) {
|
|
1314
|
+
if (_this.buttonClick) {
|
|
1315
|
+
var label = component.buttonsGroups
|
|
1316
|
+
? (event.target && event.target.ej2_instances ? event.target.ej2_instances[0].content : '')
|
|
1317
|
+
: component.label || '';
|
|
1318
|
+
_this.trigger('buttonClick', { fieldName: component.name, label: label, event: event });
|
|
1319
|
+
}
|
|
1320
|
+
var button = event.target;
|
|
1321
|
+
if (button.type && button.type === 'reset') {
|
|
1322
|
+
var form = button.closest('form');
|
|
1323
|
+
if (form) {
|
|
1324
|
+
form.querySelectorAll('input[type="checkbox"]').forEach(function (cb) {
|
|
1325
|
+
(cb.ej2_instances[0]).checked = false;
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
1328
|
+
_this.formState.values = {};
|
|
1329
|
+
form.reset();
|
|
1330
|
+
_this.rerenderForm();
|
|
1331
|
+
_this.validator.destroy();
|
|
1332
|
+
}
|
|
1333
|
+
};
|
|
1334
|
+
var instance = renderFormField({
|
|
1335
|
+
component: __assign({}, component, { disabled: buttonComputed }),
|
|
1336
|
+
value: undefined,
|
|
1337
|
+
onChange: function () { },
|
|
1338
|
+
locale: this.locale,
|
|
1339
|
+
onClick: handleButtonClick,
|
|
1340
|
+
ref: function (el, idx) {
|
|
1341
|
+
if (idx !== undefined) {
|
|
1342
|
+
_this.inputRefs[component.name + "_" + idx] = el;
|
|
1343
|
+
}
|
|
1344
|
+
else {
|
|
1345
|
+
_this.inputRefs[component.name] = el;
|
|
1346
|
+
}
|
|
1347
|
+
},
|
|
1348
|
+
enableRtl: this.enableRtl,
|
|
1349
|
+
target: target
|
|
1350
|
+
});
|
|
1351
|
+
if (Array.isArray(instance)) {
|
|
1352
|
+
instance.forEach(function (entry) {
|
|
1353
|
+
if (entry) {
|
|
1354
|
+
_this.renderedInstances.push(entry);
|
|
1355
|
+
}
|
|
1356
|
+
});
|
|
1357
|
+
}
|
|
1358
|
+
else if (instance) {
|
|
1359
|
+
this.renderedInstances.push(instance);
|
|
1360
|
+
}
|
|
1361
|
+
return wrapper;
|
|
1362
|
+
}
|
|
1363
|
+
case 'splitButton': {
|
|
1364
|
+
var splitButtonDisabled = ConditionalRuleEngine.isFieldDisabled(component.conditions, formValues);
|
|
1365
|
+
var computedDisabled = !!component.disabled || splitButtonDisabled;
|
|
1366
|
+
var wrapper = createElement('div', { className: 'form-group' });
|
|
1367
|
+
var target = createElement('div');
|
|
1368
|
+
target.id = component.name;
|
|
1369
|
+
target.setAttribute('name', component.id);
|
|
1370
|
+
wrapper.appendChild(target);
|
|
1371
|
+
var handleSplitButtonEvent = function (event) {
|
|
1372
|
+
if (_this.buttonClick) {
|
|
1373
|
+
_this.trigger('buttonClick', { fieldName: component.name, label: component.label || '', event: event });
|
|
1374
|
+
}
|
|
1375
|
+
};
|
|
1376
|
+
var instance = renderFormField({
|
|
1377
|
+
component: __assign({}, component, { disabled: computedDisabled }),
|
|
1378
|
+
value: undefined,
|
|
1379
|
+
locale: this.locale,
|
|
1380
|
+
onChange: function () { },
|
|
1381
|
+
onClick: handleSplitButtonEvent,
|
|
1382
|
+
ref: function (el) { if (el) {
|
|
1383
|
+
_this.inputRefs[component.name] = el;
|
|
1384
|
+
} },
|
|
1385
|
+
enableRtl: this.enableRtl,
|
|
1386
|
+
target: target
|
|
1387
|
+
});
|
|
1388
|
+
if (Array.isArray(instance)) {
|
|
1389
|
+
instance.forEach(function (entry) {
|
|
1390
|
+
if (entry) {
|
|
1391
|
+
_this.renderedInstances.push(entry);
|
|
1392
|
+
}
|
|
1393
|
+
});
|
|
1394
|
+
}
|
|
1395
|
+
else if (instance) {
|
|
1396
|
+
this.renderedInstances.push(instance);
|
|
1397
|
+
}
|
|
1398
|
+
return wrapper;
|
|
1399
|
+
}
|
|
1400
|
+
default:
|
|
1401
|
+
return this.renderField(component);
|
|
1402
|
+
}
|
|
1403
|
+
};
|
|
1404
|
+
/**
|
|
1405
|
+
* @private
|
|
1406
|
+
*/
|
|
1407
|
+
FormRenderer.prototype.afterValueChange = function (changedFieldId) {
|
|
1408
|
+
if (!this.formState) {
|
|
1409
|
+
return;
|
|
1410
|
+
}
|
|
1411
|
+
this.currentFormValues = __assign({}, this.formState.values);
|
|
1412
|
+
this.evaluateExpressions();
|
|
1413
|
+
this.retriggerCustomValidation();
|
|
1414
|
+
this.beforeValidate(changedFieldId);
|
|
1415
|
+
this.validator.validate(changedFieldId);
|
|
1416
|
+
this.syncFormStateErrors();
|
|
1417
|
+
// Only schedule a full-form re-render when the changed field is a
|
|
1418
|
+
// conditional trigger (i.e. some other field's visibility/disabled/
|
|
1419
|
+
// required/readOnly/valueWhen references its id). Changes to fields
|
|
1420
|
+
// that nothing depends on skip the expensive DOM teardown + rebuild,
|
|
1421
|
+
// keeping keystroke latency low for non-conditional inputs.
|
|
1422
|
+
if (changedFieldId && this.isConditionalTrigger(changedFieldId)) {
|
|
1423
|
+
this.scheduleRerender(changedFieldId);
|
|
1424
|
+
}
|
|
1425
|
+
};
|
|
1426
|
+
/**
|
|
1427
|
+
* @private
|
|
1428
|
+
*/
|
|
1429
|
+
FormRenderer.prototype.beforeValidate = function (fieldId) {
|
|
1430
|
+
var infoElement = this.element.querySelector("#" + fieldId + "-info");
|
|
1431
|
+
if (infoElement && infoElement.classList.contains('e-error')) {
|
|
1432
|
+
infoElement.classList.remove('e-error');
|
|
1433
|
+
}
|
|
1434
|
+
var inputElement = this.getInputElementByName(fieldId);
|
|
1435
|
+
var inputParent = inputElement.parentElement;
|
|
1436
|
+
var grandParent = inputParent.parentElement;
|
|
1437
|
+
if (inputParent.classList.contains('e-control-wrapper') || inputParent.classList.contains('e-wrapper') ||
|
|
1438
|
+
(inputElement.classList.contains('e-input') && inputParent.classList.contains('e-input-group'))) {
|
|
1439
|
+
inputParent.classList.remove('e-error');
|
|
1440
|
+
}
|
|
1441
|
+
else if ((grandParent != null) && (grandParent.classList.contains('e-control-wrapper') || grandParent.classList.contains('e-wrapper'))) {
|
|
1442
|
+
grandParent.classList.remove('e-error');
|
|
1443
|
+
}
|
|
1444
|
+
else {
|
|
1445
|
+
inputElement.classList.remove('e-error');
|
|
1446
|
+
}
|
|
1447
|
+
if (this.validator) {
|
|
1448
|
+
this.validator.errorRules = [];
|
|
1449
|
+
}
|
|
1450
|
+
};
|
|
1451
|
+
FormRenderer.prototype.scheduleRerender = function (changedFieldId) {
|
|
1452
|
+
var _this = this;
|
|
1453
|
+
if (changedFieldId) {
|
|
1454
|
+
this.pendingChangedFieldIds.add(changedFieldId);
|
|
1455
|
+
}
|
|
1456
|
+
if (this.rerenderScheduled) {
|
|
1457
|
+
return;
|
|
1458
|
+
}
|
|
1459
|
+
this.rerenderScheduled = true;
|
|
1460
|
+
Promise.resolve().then(function () {
|
|
1461
|
+
_this.rerenderScheduled = false;
|
|
1462
|
+
var changedIds = new Set(_this.pendingChangedFieldIds);
|
|
1463
|
+
_this.pendingChangedFieldIds.clear();
|
|
1464
|
+
_this.rerenderForm(changedIds);
|
|
1465
|
+
});
|
|
1466
|
+
};
|
|
1467
|
+
/**
|
|
1468
|
+
* @private
|
|
1469
|
+
*
|
|
1470
|
+
* Recursively walk a `ConditionType` (a `ConditionalRule { field }` leaf
|
|
1471
|
+
* or a `CompositeConditionalRule { rules: [...] }` tree) and collect the
|
|
1472
|
+
* `field` ids of every leaf rule into the supplied `acc` set. These are
|
|
1473
|
+
* the trigger ids whose value changes can affect the visibility/disabled/
|
|
1474
|
+
* required/readOnly/valueWhen of the component that owns this `conditions`.
|
|
1475
|
+
*/
|
|
1476
|
+
FormRenderer.prototype.collectConditionFieldIds = function (condition, acc) {
|
|
1477
|
+
if (!condition) {
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1480
|
+
if (typeof condition.field === 'string') {
|
|
1481
|
+
// Leaf rule.
|
|
1482
|
+
acc.add(condition.field);
|
|
1483
|
+
}
|
|
1484
|
+
else if (Array.isArray(condition.rules)) {
|
|
1485
|
+
// Composite (and/or) — recurse.
|
|
1486
|
+
for (var _i = 0, _a = condition.rules; _i < _a.length; _i++) {
|
|
1487
|
+
var sub = _a[_i];
|
|
1488
|
+
this.collectConditionFieldIds(sub, acc);
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
};
|
|
1492
|
+
FormRenderer.prototype.collectDataSourceReferenceIds = function (value, allComps, target) {
|
|
1493
|
+
if (typeof value !== 'string') {
|
|
1494
|
+
return;
|
|
1495
|
+
}
|
|
1496
|
+
var matches = value.match(/\{([^}]+)\}/g);
|
|
1497
|
+
if (!matches) {
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
var _loop_2 = function (match) {
|
|
1501
|
+
var componentName = match.slice(1, -1);
|
|
1502
|
+
var referencedComp = allComps.find(function (c) { return c.name === componentName; });
|
|
1503
|
+
if (referencedComp && referencedComp.id) {
|
|
1504
|
+
target.add(referencedComp.id);
|
|
1505
|
+
}
|
|
1506
|
+
};
|
|
1507
|
+
for (var _i = 0, matches_1 = matches; _i < matches_1.length; _i++) {
|
|
1508
|
+
var match = matches_1[_i];
|
|
1509
|
+
_loop_2(match);
|
|
1510
|
+
}
|
|
1511
|
+
};
|
|
1512
|
+
/**
|
|
1513
|
+
* @private
|
|
1514
|
+
*
|
|
1515
|
+
* Build `this.conditionalTriggerFieldIds` — the set of every
|
|
1516
|
+
* `component.id` that is referenced by *any* other component's
|
|
1517
|
+
* `conditions` (visibleWhen / hideWhen / disabledWhen / requiredWhen /
|
|
1518
|
+
* readOnlyWhen / setValueWhen / choiceBasedField.primaryFieldId).
|
|
1519
|
+
* Only changes to ids in this set can affect another field's
|
|
1520
|
+
* visibility/enabled/required/value, so only they need to trigger a
|
|
1521
|
+
* full-form re-render. Called once per schema during `render()`.
|
|
1522
|
+
*/
|
|
1523
|
+
FormRenderer.prototype.buildConditionalDependencyMap = function () {
|
|
1524
|
+
var triggers = new Set();
|
|
1525
|
+
// Per-consumer dependency set (rebuilt each pass).
|
|
1526
|
+
var consumerDeps = new Map();
|
|
1527
|
+
var allComps = this.collectAllComponents(this.processedSchemaCache.components);
|
|
1528
|
+
for (var _i = 0, allComps_1 = allComps; _i < allComps_1.length; _i++) {
|
|
1529
|
+
var comp = allComps_1[_i];
|
|
1530
|
+
var myDeps = new Set();
|
|
1531
|
+
consumerDeps.set(comp.id, myDeps);
|
|
1532
|
+
var conditions = comp.conditions;
|
|
1533
|
+
if (conditions) {
|
|
1534
|
+
var conditionKeys = [
|
|
1535
|
+
'visibleWhen',
|
|
1536
|
+
'hideWhen',
|
|
1537
|
+
'disabledWhen',
|
|
1538
|
+
'requiredWhen',
|
|
1539
|
+
'readOnlyWhen',
|
|
1540
|
+
'choiceBasedField',
|
|
1541
|
+
'conditionalData'
|
|
1542
|
+
];
|
|
1543
|
+
for (var _a = 0, conditionKeys_1 = conditionKeys; _a < conditionKeys_1.length; _a++) {
|
|
1544
|
+
var key = conditionKeys_1[_a];
|
|
1545
|
+
if (conditions[key]) {
|
|
1546
|
+
this.collectConditionFieldIds(conditions[key], triggers);
|
|
1547
|
+
this.collectConditionFieldIds(conditions[key], myDeps);
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
if (Array.isArray(conditions.conditionalData)) {
|
|
1551
|
+
for (var _b = 0, _c = conditions.conditionalData; _b < _c.length; _b++) {
|
|
1552
|
+
var item = _c[_b];
|
|
1553
|
+
if (item && item.condition) {
|
|
1554
|
+
this.collectConditionFieldIds(item.condition, triggers);
|
|
1555
|
+
this.collectConditionFieldIds(item.condition, myDeps);
|
|
1556
|
+
}
|
|
1557
|
+
// DataGrid conditional columns
|
|
1558
|
+
if (item && Array.isArray(item.conditions)) {
|
|
1559
|
+
for (var _d = 0, _e = item.conditions; _d < _e.length; _d++) {
|
|
1560
|
+
var subCond = _e[_d];
|
|
1561
|
+
this.collectConditionFieldIds(subCond, triggers);
|
|
1562
|
+
this.collectConditionFieldIds(subCond, myDeps);
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
if (conditions.setValueWhen &&
|
|
1568
|
+
conditions.setValueWhen.condition) {
|
|
1569
|
+
this.collectConditionFieldIds(conditions.setValueWhen.condition, triggers);
|
|
1570
|
+
this.collectConditionFieldIds(conditions.setValueWhen.condition, myDeps);
|
|
1571
|
+
}
|
|
1572
|
+
if (conditions.choiceBasedField &&
|
|
1573
|
+
typeof conditions.choiceBasedField.primaryFieldId === 'string') {
|
|
1574
|
+
triggers.add(conditions.choiceBasedField.primaryFieldId);
|
|
1575
|
+
myDeps.add(conditions.choiceBasedField.primaryFieldId);
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
// ==========================================================
|
|
1579
|
+
// DataGrid remote datasource dependencies
|
|
1580
|
+
// Example:
|
|
1581
|
+
// search.key = "{combobox}"
|
|
1582
|
+
// filter.value = "{department}"
|
|
1583
|
+
// Resolve component by NAME and add its ID as dependency.
|
|
1584
|
+
// ==========================================================
|
|
1585
|
+
var ds = comp.dataSource;
|
|
1586
|
+
if ((comp.type === 'dataGrid') &&
|
|
1587
|
+
ds &&
|
|
1588
|
+
ds.type === 'url') {
|
|
1589
|
+
if (ds.search && typeof ds.search.key === 'string') {
|
|
1590
|
+
this.collectDataSourceReferenceIds(ds.search.key, allComps, triggers);
|
|
1591
|
+
this.collectDataSourceReferenceIds(ds.search.key, allComps, myDeps);
|
|
1592
|
+
}
|
|
1593
|
+
if (Array.isArray(ds.filters)) {
|
|
1594
|
+
for (var _f = 0, _g = ds.filters; _f < _g.length; _f++) {
|
|
1595
|
+
var filter = _g[_f];
|
|
1596
|
+
if (typeof filter.value === 'string') {
|
|
1597
|
+
this.collectDataSourceReferenceIds(filter.value, allComps, triggers);
|
|
1598
|
+
this.collectDataSourceReferenceIds(filter.value, allComps, myDeps);
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
this.conditionalTriggerFieldIds = triggers;
|
|
1605
|
+
this.consumerDependencyMap = consumerDeps;
|
|
1606
|
+
};
|
|
1607
|
+
/**
|
|
1608
|
+
* @private
|
|
1609
|
+
*
|
|
1610
|
+
* Returns `true` if a value change to `fieldId` can affect any other
|
|
1611
|
+
* field's conditional state (visibility/disabled/required/readOnly/
|
|
1612
|
+
* valueWhen/choiceBasedField) — i.e. `fieldId` is in the
|
|
1613
|
+
* `conditionalTriggerFieldIds` set built by
|
|
1614
|
+
* `buildConditionalDependencyMap`. Used by `afterValueChange` to skip
|
|
1615
|
+
* the expensive full-form re-render when the changed field is not a
|
|
1616
|
+
* conditional trigger.
|
|
1617
|
+
*/
|
|
1618
|
+
FormRenderer.prototype.isConditionalTrigger = function (fieldId) {
|
|
1619
|
+
return this.conditionalTriggerFieldIds.has(fieldId);
|
|
1620
|
+
};
|
|
1621
|
+
/**
|
|
1622
|
+
* @private
|
|
1623
|
+
*
|
|
1624
|
+
* Tear down the rendered tree and re-render it, preserving user-entered
|
|
1625
|
+
* `formState.values` (carried through `buildValidationRulesAndBridge`'s
|
|
1626
|
+
* `previousValues` merge) and restoring keyboard focus to the field that
|
|
1627
|
+
* had it before re-render (so a user typing in a textbox is not
|
|
1628
|
+
* interrupted by a dependent field appearing/disappearing on the page).
|
|
1629
|
+
*/
|
|
1630
|
+
FormRenderer.prototype.rerenderForm = function (changedFieldIds) {
|
|
1631
|
+
var _this = this;
|
|
1632
|
+
var changedIds = changedFieldIds || new Set();
|
|
1633
|
+
var activeEl = document.activeElement;
|
|
1634
|
+
var focusedName = activeEl && activeEl.getAttribute
|
|
1635
|
+
? activeEl.getAttribute('name')
|
|
1636
|
+
: null;
|
|
1637
|
+
var selectionStart = null;
|
|
1638
|
+
var selectionEnd = null;
|
|
1639
|
+
if (focusedName && activeEl && activeEl.selectionStart !== undefined) {
|
|
1640
|
+
try {
|
|
1641
|
+
selectionStart = activeEl.selectionStart;
|
|
1642
|
+
selectionEnd = activeEl.selectionEnd;
|
|
1643
|
+
}
|
|
1644
|
+
catch (e) { /* not a text input — ignore */ }
|
|
1645
|
+
}
|
|
1646
|
+
var savedElementScrollTop = this.element.scrollTop;
|
|
1647
|
+
var savedElementScrollLeft = this.element.scrollLeft;
|
|
1648
|
+
var savedWindowScrollX = window.scrollX;
|
|
1649
|
+
var savedWindowScrollY = window.scrollY;
|
|
1650
|
+
this.preservedFields.clear();
|
|
1651
|
+
var allComps = this.collectAllComponents(this.processedSchemaCache.components);
|
|
1652
|
+
var _loop_3 = function (comp) {
|
|
1653
|
+
if (!FormRenderer_1.PRESERVABLE_TYPES.has(comp.type)) {
|
|
1654
|
+
return "continue";
|
|
1655
|
+
}
|
|
1656
|
+
var myDeps = this_1.consumerDependencyMap.get(comp.id);
|
|
1657
|
+
var dependsOnChanged = false;
|
|
1658
|
+
if (myDeps) {
|
|
1659
|
+
changedIds.forEach(function (changedId) {
|
|
1660
|
+
if (myDeps.has(changedId)) {
|
|
1661
|
+
dependsOnChanged = true;
|
|
1662
|
+
}
|
|
1663
|
+
});
|
|
1664
|
+
}
|
|
1665
|
+
if (dependsOnChanged) {
|
|
1666
|
+
return "continue";
|
|
1667
|
+
}
|
|
1668
|
+
var liveNode = this_1.element.querySelector("[data-field-id=\"" + comp.id + "\"]");
|
|
1669
|
+
if (liveNode && liveNode.parentElement) {
|
|
1670
|
+
liveNode.parentElement.removeChild(liveNode);
|
|
1671
|
+
this_1.preservedFields.set(comp.id, liveNode);
|
|
1672
|
+
}
|
|
1673
|
+
};
|
|
1674
|
+
var this_1 = this;
|
|
1675
|
+
for (var _i = 0, allComps_2 = allComps; _i < allComps_2.length; _i++) {
|
|
1676
|
+
var comp = allComps_2[_i];
|
|
1677
|
+
_loop_3(comp);
|
|
1678
|
+
}
|
|
1679
|
+
this.refreshing = true;
|
|
1680
|
+
this.destroyRenderedExcludingPreserved();
|
|
1681
|
+
this.removePreviousRoot();
|
|
1682
|
+
if (this.validator) {
|
|
1683
|
+
this.validator.destroy();
|
|
1684
|
+
}
|
|
1685
|
+
this.render();
|
|
1686
|
+
this.refreshing = false;
|
|
1687
|
+
try {
|
|
1688
|
+
this.element.scrollTop = savedElementScrollTop;
|
|
1689
|
+
this.element.scrollLeft = savedElementScrollLeft;
|
|
1690
|
+
window.scrollTo(savedWindowScrollX, savedWindowScrollY);
|
|
1691
|
+
}
|
|
1692
|
+
catch (e) { /* swallow */ }
|
|
1693
|
+
if (focusedName) {
|
|
1694
|
+
var elToFocus = this.getInputElementByName(focusedName);
|
|
1695
|
+
if (elToFocus && typeof elToFocus.focus === 'function') {
|
|
1696
|
+
try {
|
|
1697
|
+
elToFocus.focus();
|
|
1698
|
+
if (selectionStart !== null && selectionEnd !== null &&
|
|
1699
|
+
typeof elToFocus.setSelectionRange === 'function') {
|
|
1700
|
+
try {
|
|
1701
|
+
elToFocus.setSelectionRange(selectionStart, selectionEnd);
|
|
1702
|
+
}
|
|
1703
|
+
catch (e) { /* swallow — non-text input */ }
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
catch (e) { /* swallow */ }
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
// Drop preserved subtrees that were not re-attached (e.g. their
|
|
1710
|
+
// field became hidden during this re-render). Their EJ2 instances
|
|
1711
|
+
// are still in `renderedInstances`; destroy them now to avoid leaks.
|
|
1712
|
+
if (this.preservedFields.size > 0) {
|
|
1713
|
+
var stillKept_1 = [];
|
|
1714
|
+
this.preservedFields.forEach(function (node, id) {
|
|
1715
|
+
// If the node is no longer in the live document tree, the
|
|
1716
|
+
// field was not re-attached (hidden / removed). Destroy it
|
|
1717
|
+
// and remove the detached DOM node so it cannot be re-used.
|
|
1718
|
+
if (!_this.element.contains(node) && !document.body.contains(node)) {
|
|
1719
|
+
_this.destroyPreservedSubtree(node);
|
|
1720
|
+
try {
|
|
1721
|
+
if (node.parentNode) {
|
|
1722
|
+
node.parentNode.removeChild(node);
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
catch (e) {
|
|
1726
|
+
// swallow
|
|
1727
|
+
}
|
|
1728
|
+
_this.preservedFields.delete(id);
|
|
1729
|
+
}
|
|
1730
|
+
else {
|
|
1731
|
+
stillKept_1.push(id);
|
|
1732
|
+
}
|
|
1733
|
+
});
|
|
1734
|
+
stillKept_1.forEach(function (id) { return _this.preservedFields.delete(id); });
|
|
1735
|
+
}
|
|
1736
|
+
};
|
|
1737
|
+
/**
|
|
1738
|
+
* @private
|
|
1739
|
+
*/
|
|
1740
|
+
FormRenderer.prototype.removePreviousRoot = function () {
|
|
1741
|
+
if (this.element) {
|
|
1742
|
+
while (this.element.childNodes.length > 0) {
|
|
1743
|
+
this.element.removeChild(this.element.firstChild);
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
};
|
|
1747
|
+
/**
|
|
1748
|
+
* @private
|
|
1749
|
+
*/
|
|
1750
|
+
FormRenderer.prototype.destroyRenderedInstance = function (inst) {
|
|
1751
|
+
if (!inst || typeof inst.destroy !== 'function') {
|
|
1752
|
+
return;
|
|
1753
|
+
}
|
|
1754
|
+
var state = inst;
|
|
1755
|
+
if (state.isDestroyed || state.destroyed || state.disposed) {
|
|
1756
|
+
return;
|
|
1757
|
+
}
|
|
1758
|
+
var hostElement = inst.element || inst.target || (inst.hostElement && typeof inst.hostElement === 'function' ? inst.hostElement() : undefined);
|
|
1759
|
+
try {
|
|
1760
|
+
state.isDestroyed = true;
|
|
1761
|
+
state.destroyed = true;
|
|
1762
|
+
state.disposed = true;
|
|
1763
|
+
inst.destroy();
|
|
1764
|
+
}
|
|
1765
|
+
catch (e) {
|
|
1766
|
+
console.warn('FormRenderer: failed to destroy child instance.', e);
|
|
1767
|
+
}
|
|
1768
|
+
finally {
|
|
1769
|
+
if (hostElement && hostElement.parentNode) {
|
|
1770
|
+
try {
|
|
1771
|
+
hostElement.parentNode.removeChild(hostElement);
|
|
1772
|
+
}
|
|
1773
|
+
catch (e) {
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
};
|
|
1778
|
+
/**
|
|
1779
|
+
* @private
|
|
1780
|
+
*
|
|
1781
|
+
* Destroy all rendered EJ2 instances except those whose host element
|
|
1782
|
+
* lives inside a preserved subtree (those are kept alive for re-use in
|
|
1783
|
+
* the upcoming `render()` pass). Mirrors `destroyRendered` but skips
|
|
1784
|
+
* instances rooted in `preservedFields` nodes.
|
|
1785
|
+
*/
|
|
1786
|
+
FormRenderer.prototype.destroyRenderedExcludingPreserved = function () {
|
|
1787
|
+
var survived = [];
|
|
1788
|
+
var preservedNodes = Array.from(this.preservedFields.values());
|
|
1789
|
+
var _loop_4 = function (inst) {
|
|
1790
|
+
var hostEl = inst && (inst.element || inst.target || (inst.hostElement && inst.hostElement()));
|
|
1791
|
+
var isPreserved = !!hostEl && preservedNodes.some(function (n) { return n === hostEl || n.contains(hostEl); });
|
|
1792
|
+
if (isPreserved) {
|
|
1793
|
+
survived.push(inst);
|
|
1794
|
+
}
|
|
1795
|
+
else {
|
|
1796
|
+
this_2.destroyRenderedInstance(inst);
|
|
1797
|
+
}
|
|
1798
|
+
};
|
|
1799
|
+
var this_2 = this;
|
|
1800
|
+
for (var _i = 0, _a = this.renderedInstances; _i < _a.length; _i++) {
|
|
1801
|
+
var inst = _a[_i];
|
|
1802
|
+
_loop_4(inst);
|
|
1803
|
+
}
|
|
1804
|
+
this.renderedInstances = survived;
|
|
1805
|
+
// Prune inputRefs whose host element lives in a destroyed subtree
|
|
1806
|
+
// (preserved ones stay).
|
|
1807
|
+
var inputRefKeys = Object.keys(this.inputRefs);
|
|
1808
|
+
var _loop_5 = function (key) {
|
|
1809
|
+
var inst = this_3.inputRefs[key];
|
|
1810
|
+
var hostEl = inst && (inst.element || inst.target || (inst.hostElement && inst.hostElement()));
|
|
1811
|
+
var isPreserved = !!hostEl && preservedNodes.some(function (n) { return n === hostEl || n.contains(hostEl); });
|
|
1812
|
+
if (!isPreserved) {
|
|
1813
|
+
delete this_3.inputRefs[key];
|
|
1814
|
+
}
|
|
1815
|
+
};
|
|
1816
|
+
var this_3 = this;
|
|
1817
|
+
for (var _b = 0, inputRefKeys_1 = inputRefKeys; _b < inputRefKeys_1.length; _b++) {
|
|
1818
|
+
var key = inputRefKeys_1[_b];
|
|
1819
|
+
_loop_5(key);
|
|
1820
|
+
}
|
|
1821
|
+
this.conditionalValueApplied = {};
|
|
1822
|
+
this.expressionDependencyGraph = {};
|
|
1823
|
+
this.computedFieldNames = new Set();
|
|
1824
|
+
this.previousFormValues = {};
|
|
1825
|
+
this.conditionalTriggerFieldIds = new Set();
|
|
1826
|
+
};
|
|
1827
|
+
/**
|
|
1828
|
+
* @private
|
|
1829
|
+
*
|
|
1830
|
+
* Destroy EJ2 instances whose host element lives inside a preserved
|
|
1831
|
+
* subtree that was NOT re-attached (field became hidden). Used during
|
|
1832
|
+
* the post-render cleanup pass in `rerenderForm`.
|
|
1833
|
+
*/
|
|
1834
|
+
FormRenderer.prototype.destroyPreservedSubtree = function (rootNode) {
|
|
1835
|
+
var survived = [];
|
|
1836
|
+
for (var _i = 0, _a = this.renderedInstances; _i < _a.length; _i++) {
|
|
1837
|
+
var inst = _a[_i];
|
|
1838
|
+
var hostEl = inst && (inst.element || inst.target || (inst.hostElement && inst.hostElement()));
|
|
1839
|
+
if (hostEl && (hostEl === rootNode || rootNode.contains(hostEl))) {
|
|
1840
|
+
this.destroyRenderedInstance(inst);
|
|
1841
|
+
}
|
|
1842
|
+
else {
|
|
1843
|
+
survived.push(inst);
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
this.renderedInstances = survived;
|
|
1847
|
+
};
|
|
1848
|
+
FormRenderer.prototype.getInputElementByName = function (name) {
|
|
1849
|
+
var inputEl = this.element.querySelector("[name=\"" + name + "\"]");
|
|
1850
|
+
if (!inputEl) {
|
|
1851
|
+
return null;
|
|
1852
|
+
}
|
|
1853
|
+
if (!inputEl.classList.contains('e-control')) {
|
|
1854
|
+
var eControl = inputEl.parentElement ? inputEl.parentElement.querySelector('.e-control') : null;
|
|
1855
|
+
if (eControl) {
|
|
1856
|
+
return eControl;
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
return inputEl;
|
|
1860
|
+
};
|
|
1861
|
+
/**
|
|
1862
|
+
* Evaluate expressions in topological order with type coercion.
|
|
1863
|
+
* @private
|
|
1864
|
+
*/
|
|
1865
|
+
FormRenderer.prototype.evaluateExpressions = function () {
|
|
1866
|
+
if (!this.formState || !this.expressionEngine) {
|
|
1867
|
+
return;
|
|
1868
|
+
}
|
|
1869
|
+
if (this.expressionEvaluationInProgress) {
|
|
1870
|
+
return;
|
|
1871
|
+
}
|
|
1872
|
+
try {
|
|
1873
|
+
this.expressionEvaluationInProgress = true;
|
|
1874
|
+
var engine = this.expressionEngine;
|
|
1875
|
+
var dependencyGraph = this.expressionDependencyGraph;
|
|
1876
|
+
var formValues = this.formState.values || {};
|
|
1877
|
+
var allComponents = this.collectAllComponents(this.processedSchemaCache.components);
|
|
1878
|
+
var previousValues = this.previousFormValues;
|
|
1879
|
+
var computedFieldNames = this.computedFieldNames;
|
|
1880
|
+
var idToNameMap_1 = {};
|
|
1881
|
+
allComponents.forEach(function (comp) {
|
|
1882
|
+
if (comp.name) {
|
|
1883
|
+
idToNameMap_1[comp.id] = comp.name;
|
|
1884
|
+
}
|
|
1885
|
+
});
|
|
1886
|
+
var contextValues = {};
|
|
1887
|
+
var fvKeys = Object.keys(formValues);
|
|
1888
|
+
for (var _i = 0, fvKeys_1 = fvKeys; _i < fvKeys_1.length; _i++) {
|
|
1889
|
+
var componentId = fvKeys_1[_i];
|
|
1890
|
+
var value = formValues[componentId];
|
|
1891
|
+
var fieldName = idToNameMap_1[componentId];
|
|
1892
|
+
if (fieldName) {
|
|
1893
|
+
contextValues[fieldName] = value;
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
var previousContextValues = __assign({}, previousValues);
|
|
1897
|
+
Object.assign(previousValues, contextValues);
|
|
1898
|
+
var changedFields = new Set();
|
|
1899
|
+
var ctxKeys = Object.keys(contextValues);
|
|
1900
|
+
var _loop_6 = function (fieldName) {
|
|
1901
|
+
var fieldId = Object.keys(idToNameMap_1).find(function (id) { return idToNameMap_1[id] === fieldName; });
|
|
1902
|
+
if (fieldId && contextValues[fieldName] !== previousContextValues[fieldName] && !computedFieldNames.has(fieldId)) {
|
|
1903
|
+
changedFields.add(fieldName);
|
|
1904
|
+
}
|
|
1905
|
+
};
|
|
1906
|
+
for (var _a = 0, ctxKeys_1 = ctxKeys; _a < ctxKeys_1.length; _a++) {
|
|
1907
|
+
var fieldName = ctxKeys_1[_a];
|
|
1908
|
+
_loop_6(fieldName);
|
|
1909
|
+
}
|
|
1910
|
+
if (changedFields.size === 0) {
|
|
1911
|
+
return;
|
|
1912
|
+
}
|
|
1913
|
+
var evaluationOrder = engine.getTopologicalOrder(dependencyGraph);
|
|
1914
|
+
var _loop_7 = function (fieldName) {
|
|
1915
|
+
var component = allComponents.find(function (c) { return c.name === fieldName; });
|
|
1916
|
+
if (component && component.expressionValue) {
|
|
1917
|
+
computedFieldNames.add(component.id);
|
|
1918
|
+
try {
|
|
1919
|
+
var isVisible = component.visible !== false
|
|
1920
|
+
? ConditionalRuleEngine.isFieldVisible(component.conditions, formValues)
|
|
1921
|
+
: false;
|
|
1922
|
+
if (isVisible) {
|
|
1923
|
+
var currentValues = {};
|
|
1924
|
+
var fvKeys2 = Object.keys(formValues);
|
|
1925
|
+
for (var _i = 0, fvKeys2_1 = fvKeys2; _i < fvKeys2_1.length; _i++) {
|
|
1926
|
+
var componentId = fvKeys2_1[_i];
|
|
1927
|
+
var value = formValues[componentId];
|
|
1928
|
+
var fn = idToNameMap_1[componentId];
|
|
1929
|
+
if (fn) {
|
|
1930
|
+
currentValues[fn] = value;
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
var result = engine.evaluate(component.expressionValue, currentValues);
|
|
1934
|
+
result = (typeof result === 'string' && result.indexOf('undefined') !== -1)
|
|
1935
|
+
? result.replace(/undefined/g, '')
|
|
1936
|
+
: result;
|
|
1937
|
+
if (result !== null && result !== undefined && result !== '') {
|
|
1938
|
+
switch (component.type) {
|
|
1939
|
+
case 'number': {
|
|
1940
|
+
var numResult = Number(result);
|
|
1941
|
+
result = isNaN(numResult) ? 0 : numResult;
|
|
1942
|
+
break;
|
|
1943
|
+
}
|
|
1944
|
+
case 'checkbox':
|
|
1945
|
+
case 'switch':
|
|
1946
|
+
result = Boolean(result);
|
|
1947
|
+
break;
|
|
1948
|
+
case 'textbox':
|
|
1949
|
+
case 'textarea':
|
|
1950
|
+
result = String(result);
|
|
1951
|
+
break;
|
|
1952
|
+
case 'dateRange':
|
|
1953
|
+
case 'checkboxGroup':
|
|
1954
|
+
case 'multiselect':
|
|
1955
|
+
break;
|
|
1956
|
+
default:
|
|
1957
|
+
result = String(result);
|
|
1958
|
+
break;
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
if (result !== null && result !== undefined && result !== formValues[component.id] && this_4.formState) {
|
|
1962
|
+
this_4.formState.values[component.id] = result;
|
|
1963
|
+
// Sync the underlying input element so the
|
|
1964
|
+
// FormValidator reads the new value on its
|
|
1965
|
+
// next validate() pass.
|
|
1966
|
+
if (this_4.validator) {
|
|
1967
|
+
var inputEl = this_4.getInputElementByName(component.id);
|
|
1968
|
+
if (inputEl) {
|
|
1969
|
+
try {
|
|
1970
|
+
if (inputEl.ej2_instances[0]) {
|
|
1971
|
+
inputEl.ej2_instances[0].value = result;
|
|
1972
|
+
// (inputEl as any).ej2_instances[0].refresh();
|
|
1973
|
+
}
|
|
1974
|
+
else {
|
|
1975
|
+
(inputEl).value = String(result);
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
catch (e) { /* swallow */ }
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
catch (error) {
|
|
1985
|
+
if (this_4.failure) {
|
|
1986
|
+
this_4.trigger('failure', {
|
|
1987
|
+
error: error
|
|
1988
|
+
});
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
};
|
|
1993
|
+
var this_4 = this;
|
|
1994
|
+
for (var _b = 0, evaluationOrder_1 = evaluationOrder; _b < evaluationOrder_1.length; _b++) {
|
|
1995
|
+
var fieldName = evaluationOrder_1[_b];
|
|
1996
|
+
_loop_7(fieldName);
|
|
1997
|
+
}
|
|
1998
|
+
Object.assign(previousValues, contextValues);
|
|
1999
|
+
}
|
|
2000
|
+
finally {
|
|
2001
|
+
this.expressionEvaluationInProgress = false;
|
|
2002
|
+
}
|
|
2003
|
+
};
|
|
2004
|
+
/**
|
|
2005
|
+
* Re-trigger custom validation for changed + dependent fields.
|
|
2006
|
+
* @private
|
|
2007
|
+
*/
|
|
2008
|
+
FormRenderer.prototype.retriggerCustomValidation = function () {
|
|
2009
|
+
var _this = this;
|
|
2010
|
+
if (!this.formState) {
|
|
2011
|
+
return;
|
|
2012
|
+
}
|
|
2013
|
+
if (!this.customValidationEngine) {
|
|
2014
|
+
return;
|
|
2015
|
+
}
|
|
2016
|
+
try {
|
|
2017
|
+
var allComponents_1 = this.collectAllComponents(this.processedSchemaCache.components);
|
|
2018
|
+
var formValues_1 = this.formState.values || {};
|
|
2019
|
+
var previousValues_1 = this.previousFormValues;
|
|
2020
|
+
var changedFields_1 = new Set();
|
|
2021
|
+
Object.keys(formValues_1).forEach(function (fieldId) {
|
|
2022
|
+
if (formValues_1[fieldId] !== previousValues_1[fieldId]) {
|
|
2023
|
+
changedFields_1.add(fieldId);
|
|
2024
|
+
}
|
|
2025
|
+
});
|
|
2026
|
+
if (changedFields_1.size === 0) {
|
|
2027
|
+
return;
|
|
2028
|
+
}
|
|
2029
|
+
var fieldsToValidate_1 = new Set();
|
|
2030
|
+
changedFields_1.forEach(function (fieldId) {
|
|
2031
|
+
var component = allComponents_1.find(function (c) { return c.id === fieldId; });
|
|
2032
|
+
if (component && component.customValidation && Array.isArray(component.customValidation)) {
|
|
2033
|
+
fieldsToValidate_1.add(fieldId);
|
|
2034
|
+
}
|
|
2035
|
+
});
|
|
2036
|
+
allComponents_1.forEach(function (component) {
|
|
2037
|
+
if (component.customValidation && Array.isArray(component.customValidation)) {
|
|
2038
|
+
component.customValidation.forEach(function (rule) {
|
|
2039
|
+
var deps = _this.customValidationEngine.extractDependencies(rule.expression);
|
|
2040
|
+
deps.forEach(function (dep) {
|
|
2041
|
+
var depComponent = allComponents_1.find(function (c) { return c.id === dep || c.name === dep; });
|
|
2042
|
+
if (depComponent && changedFields_1.has(depComponent.id)) {
|
|
2043
|
+
fieldsToValidate_1.add(component.id);
|
|
2044
|
+
}
|
|
2045
|
+
});
|
|
2046
|
+
});
|
|
2047
|
+
}
|
|
2048
|
+
});
|
|
2049
|
+
this.previousFormValues = __assign({}, formValues_1);
|
|
2050
|
+
}
|
|
2051
|
+
catch (error) {
|
|
2052
|
+
if (this.failure) {
|
|
2053
|
+
this.trigger('failure', {
|
|
2054
|
+
error: error
|
|
2055
|
+
});
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
};
|
|
2059
|
+
/**
|
|
2060
|
+
* @private
|
|
2061
|
+
*/
|
|
2062
|
+
FormRenderer.prototype.handleSubmitData = function (data) {
|
|
2063
|
+
var formState = this.formState;
|
|
2064
|
+
var isValid = formState && formState.errors ? Object.keys(formState.errors).length === 0 : false;
|
|
2065
|
+
var transformedData = {};
|
|
2066
|
+
var formValues = formState ? formState.values : {};
|
|
2067
|
+
var excludedTypes = new Set(['button', 'message', 'panel', 'table', 'tabs']);
|
|
2068
|
+
var allComps = this.collectAllComponents(this.processedSchemaCache.components || []);
|
|
2069
|
+
var excludedIds = new Set(allComps.filter(function (c) { return excludedTypes.has(c.type); }).map(function (c) { return c.id; }));
|
|
2070
|
+
var hiddenFieldIds = new Set(allComps.filter(function (c) {
|
|
2071
|
+
if (c.visible === false) {
|
|
2072
|
+
return true;
|
|
2073
|
+
}
|
|
2074
|
+
if (c.conditions && c.conditions.visibleWhen) {
|
|
2075
|
+
if (!ConditionalRuleEngine.isFieldVisible(c.conditions, formValues)) {
|
|
2076
|
+
return true;
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
if (c.conditions && c.conditions.hideWhen) {
|
|
2080
|
+
if (ConditionalRuleEngine.isFieldHidden(c.conditions, formValues)) {
|
|
2081
|
+
return true;
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
return false;
|
|
2085
|
+
}).map(function (c) { return c.id; }));
|
|
2086
|
+
var disabledFieldIds = new Set(allComps.filter(function (c) {
|
|
2087
|
+
if (c.disabled) {
|
|
2088
|
+
return true;
|
|
2089
|
+
}
|
|
2090
|
+
if (c.conditions && c.conditions.disabledWhen) {
|
|
2091
|
+
return ConditionalRuleEngine.isFieldDisabled(c.conditions, formValues);
|
|
2092
|
+
}
|
|
2093
|
+
return false;
|
|
2094
|
+
}).map(function (c) { return c.id; }));
|
|
2095
|
+
var dataKeys = Object.keys(data);
|
|
2096
|
+
for (var _i = 0, dataKeys_1 = dataKeys; _i < dataKeys_1.length; _i++) {
|
|
2097
|
+
var id = dataKeys_1[_i];
|
|
2098
|
+
var val = data[id];
|
|
2099
|
+
if (excludedIds.has(id) || hiddenFieldIds.has(id) || disabledFieldIds.has(id)) {
|
|
2100
|
+
return;
|
|
2101
|
+
}
|
|
2102
|
+
var label = this.idToLabelCache[id] || id;
|
|
2103
|
+
transformedData[label] = val;
|
|
2104
|
+
}
|
|
2105
|
+
if (this.submit) {
|
|
2106
|
+
this.trigger('submit', { data: transformedData, isValid: isValid });
|
|
2107
|
+
}
|
|
2108
|
+
};
|
|
2109
|
+
/**
|
|
2110
|
+
* Retrieves the underlying Syncfusion form-input component instance by its field name. Use this to call Syncfusion component methods and properties.
|
|
2111
|
+
*/
|
|
2112
|
+
FormRenderer.prototype.getComponent = function (name) {
|
|
2113
|
+
return this.inputRefs[name] || null;
|
|
2114
|
+
};
|
|
2115
|
+
/**
|
|
2116
|
+
* Re-normalize + re-render on `schema`/`locale`/`enableRtl` change.
|
|
2117
|
+
* @private
|
|
2118
|
+
*/
|
|
2119
|
+
FormRenderer.prototype.onPropertyChanged = function (newProps, oldProps) {
|
|
2120
|
+
var reRender = false;
|
|
2121
|
+
if (newProps.schema !== oldProps.schema) {
|
|
2122
|
+
reRender = true;
|
|
2123
|
+
}
|
|
2124
|
+
if (newProps.locale !== oldProps.locale) {
|
|
2125
|
+
reRender = true;
|
|
2126
|
+
}
|
|
2127
|
+
if (newProps.enableRtl !== oldProps.enableRtl) {
|
|
2128
|
+
reRender = true;
|
|
2129
|
+
}
|
|
2130
|
+
if (newProps.layout !== oldProps.layout) {
|
|
2131
|
+
reRender = true;
|
|
2132
|
+
}
|
|
2133
|
+
if (newProps.locale !== oldProps.locale) {
|
|
2134
|
+
reRender = true;
|
|
2135
|
+
}
|
|
2136
|
+
if (!reRender) {
|
|
2137
|
+
return;
|
|
2138
|
+
}
|
|
2139
|
+
// Tear down rendered children and re-render.
|
|
2140
|
+
this.refreshing = true;
|
|
2141
|
+
this.destroyRendered();
|
|
2142
|
+
this.removePreviousRoot();
|
|
2143
|
+
this.isInitialized = false;
|
|
2144
|
+
this.render();
|
|
2145
|
+
this.refreshing = false;
|
|
2146
|
+
};
|
|
2147
|
+
/**
|
|
2148
|
+
* Destroy all child EJ2 instances, detach listeners, null refs.
|
|
2149
|
+
* @private
|
|
2150
|
+
*/
|
|
2151
|
+
FormRenderer.prototype.destroyRendered = function () {
|
|
2152
|
+
var _this = this;
|
|
2153
|
+
if (this.validator) {
|
|
2154
|
+
try {
|
|
2155
|
+
this.validator.destroy();
|
|
2156
|
+
}
|
|
2157
|
+
catch (e) {
|
|
2158
|
+
this.trigger('failure', {
|
|
2159
|
+
error: e
|
|
2160
|
+
});
|
|
2161
|
+
}
|
|
2162
|
+
this.validator = null;
|
|
2163
|
+
}
|
|
2164
|
+
this.renderedInstances.forEach(function (inst) {
|
|
2165
|
+
_this.destroyRenderedInstance(inst);
|
|
2166
|
+
});
|
|
2167
|
+
this.renderedInstances = [];
|
|
2168
|
+
this.removePreviousRoot();
|
|
2169
|
+
this.inputRefs = {};
|
|
2170
|
+
this.tooltipRefs = {};
|
|
2171
|
+
this.conditionalValueApplied = {};
|
|
2172
|
+
this.expressionDependencyGraph = {};
|
|
2173
|
+
this.computedFieldNames = new Set();
|
|
2174
|
+
this.previousFormValues = {};
|
|
2175
|
+
this.conditionalTriggerFieldIds = new Set();
|
|
2176
|
+
this.consumerDependencyMap = new Map();
|
|
2177
|
+
this.preservedFields = new Map();
|
|
2178
|
+
this.pendingChangedFieldIds = new Set();
|
|
2179
|
+
};
|
|
2180
|
+
/**
|
|
2181
|
+
* Remove persisted tab selection state for this form when it is fully destroyed.
|
|
2182
|
+
* @private
|
|
2183
|
+
*/
|
|
2184
|
+
FormRenderer.prototype.clearPersistedTabSelections = function () {
|
|
2185
|
+
if (typeof window === 'undefined')
|
|
2186
|
+
return null;
|
|
2187
|
+
try {
|
|
2188
|
+
var components = (this.processedSchemaCache && this.processedSchemaCache.components)
|
|
2189
|
+
? this.processedSchemaCache.components
|
|
2190
|
+
: [];
|
|
2191
|
+
var allComps = this.collectAllComponents(components);
|
|
2192
|
+
allComps.forEach(function (component) {
|
|
2193
|
+
if (component.type === 'tabs' && component.id) {
|
|
2194
|
+
window.localStorage.removeItem('tab' + component.id);
|
|
2195
|
+
}
|
|
2196
|
+
});
|
|
2197
|
+
}
|
|
2198
|
+
catch (_a) {
|
|
2199
|
+
}
|
|
2200
|
+
};
|
|
2201
|
+
/**
|
|
2202
|
+
* Full component teardown.
|
|
2203
|
+
* @private
|
|
2204
|
+
*/
|
|
2205
|
+
FormRenderer.prototype.destroy = function () {
|
|
2206
|
+
this.destroyRendered();
|
|
2207
|
+
this.formState = { values: {}, errors: {} };
|
|
2208
|
+
this.expressionEngine = null;
|
|
2209
|
+
this.customValidationEngine = null;
|
|
2210
|
+
this.clearEngineCaches();
|
|
2211
|
+
this.clearPersistedTabSelections();
|
|
2212
|
+
this.consumerDependencyMap = new Map();
|
|
2213
|
+
this.preservedFields = new Map();
|
|
2214
|
+
this.conditionalTriggerFieldIds = new Set();
|
|
2215
|
+
_super.prototype.destroy.call(this);
|
|
2216
|
+
};
|
|
2217
|
+
/**
|
|
2218
|
+
* @private
|
|
2219
|
+
*/
|
|
2220
|
+
FormRenderer.prototype.clearEngineCaches = function () {
|
|
2221
|
+
if (this.expressionEngine) {
|
|
2222
|
+
try {
|
|
2223
|
+
this.expressionEngine.clearCache();
|
|
2224
|
+
}
|
|
2225
|
+
catch (e) { /* swallow */ }
|
|
2226
|
+
}
|
|
2227
|
+
if (this.customValidationEngine) {
|
|
2228
|
+
var inner = this.customValidationEngine.expressionEngine;
|
|
2229
|
+
if (inner && typeof inner.clearCache === 'function') {
|
|
2230
|
+
try {
|
|
2231
|
+
inner.clearCache();
|
|
2232
|
+
}
|
|
2233
|
+
catch (e) { /* swallow */ }
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
};
|
|
2237
|
+
var FormRenderer_1;
|
|
2238
|
+
/**
|
|
2239
|
+
* Component types eligible for DOM preservation across re-renders.
|
|
2240
|
+
* These types either bind remote data (dataGrid, lookup-backed
|
|
2241
|
+
* dropdown/multiselect) or render many child options (checkboxGroup)
|
|
2242
|
+
* and are noticeably expensive / flicker-prone to rebuild.
|
|
2243
|
+
*/
|
|
2244
|
+
FormRenderer.PRESERVABLE_TYPES = new Set([
|
|
2245
|
+
'textbox', 'textarea', 'number', 'checkbox', 'checkboxGroup', 'radio', 'dropdown', 'multiselect', 'button', 'date', 'dateTime', 'time', 'dateRange', 'switch', 'signature', 'rating', 'imageEditor', 'fileUpload', 'rangeSlider', 'colorPicker', 'inputMask', 'message', 'panel', 'table', 'tabs', 'dataGrid', 'staticHtml', 'card', 'splitButton', 'richTextEditor'
|
|
2246
|
+
]);
|
|
2247
|
+
__decorate([
|
|
2248
|
+
Property(null)
|
|
2249
|
+
], FormRenderer.prototype, "schema", void 0);
|
|
2250
|
+
__decorate([
|
|
2251
|
+
Property('SingleColumn')
|
|
2252
|
+
], FormRenderer.prototype, "layout", void 0);
|
|
2253
|
+
__decorate([
|
|
2254
|
+
Property('')
|
|
2255
|
+
], FormRenderer.prototype, "className", void 0);
|
|
2256
|
+
__decorate([
|
|
2257
|
+
Event()
|
|
2258
|
+
], FormRenderer.prototype, "submit", void 0);
|
|
2259
|
+
__decorate([
|
|
2260
|
+
Event()
|
|
2261
|
+
], FormRenderer.prototype, "change", void 0);
|
|
2262
|
+
__decorate([
|
|
2263
|
+
Event()
|
|
2264
|
+
], FormRenderer.prototype, "buttonClick", void 0);
|
|
2265
|
+
__decorate([
|
|
2266
|
+
Event()
|
|
2267
|
+
], FormRenderer.prototype, "failure", void 0);
|
|
2268
|
+
__decorate([
|
|
2269
|
+
Event()
|
|
2270
|
+
], FormRenderer.prototype, "created", void 0);
|
|
2271
|
+
FormRenderer = FormRenderer_1 = __decorate([
|
|
2272
|
+
NotifyPropertyChanges
|
|
2273
|
+
], FormRenderer);
|
|
2274
|
+
return FormRenderer;
|
|
2275
|
+
}(Component));
|
|
2276
|
+
export { FormRenderer };
|
|
2277
|
+
export default FormRenderer;
|