@ng-formworks/core 17.2.7 → 18.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +834 -0
  3. package/esm2022/lib/framework-library/framework-library.service.mjs +175 -0
  4. package/esm2022/lib/framework-library/framework.mjs +15 -0
  5. package/esm2022/lib/framework-library/no-framework.component.mjs +18 -0
  6. package/esm2022/lib/framework-library/no-framework.module.mjs +27 -0
  7. package/esm2022/lib/framework-library/no.framework.mjs +19 -0
  8. package/esm2022/lib/json-schema-form.component.mjs +765 -0
  9. package/esm2022/lib/json-schema-form.module.mjs +26 -0
  10. package/esm2022/lib/json-schema-form.service.mjs +676 -0
  11. package/esm2022/lib/locale/de-validation-messages.mjs +60 -0
  12. package/esm2022/lib/locale/en-validation-messages.mjs +60 -0
  13. package/esm2022/lib/locale/es-validation-messages.mjs +57 -0
  14. package/esm2022/lib/locale/fr-validation-messages.mjs +60 -0
  15. package/esm2022/lib/locale/index.mjs +8 -0
  16. package/esm2022/lib/locale/it-validation-messages.mjs +60 -0
  17. package/esm2022/lib/locale/pt-validation-messages.mjs +60 -0
  18. package/esm2022/lib/locale/zh-validation-messages.mjs +60 -0
  19. package/esm2022/lib/shared/convert-schema-to-draft6.function.mjs +300 -0
  20. package/esm2022/lib/shared/form-group.functions.mjs +442 -0
  21. package/esm2022/lib/shared/format-regex.constants.mjs +54 -0
  22. package/esm2022/lib/shared/index.mjs +12 -0
  23. package/esm2022/lib/shared/json-schema.functions.mjs +784 -0
  24. package/esm2022/lib/shared/json.validators.mjs +884 -0
  25. package/esm2022/lib/shared/jsonpointer.functions.mjs +1026 -0
  26. package/esm2022/lib/shared/layout.functions.mjs +1158 -0
  27. package/esm2022/lib/shared/merge-schemas.function.mjs +345 -0
  28. package/esm2022/lib/shared/utility.functions.mjs +380 -0
  29. package/esm2022/lib/shared/validator.functions.mjs +584 -0
  30. package/esm2022/lib/widget-library/add-reference.component.mjs +61 -0
  31. package/esm2022/lib/widget-library/button.component.mjs +72 -0
  32. package/esm2022/lib/widget-library/checkbox.component.mjs +105 -0
  33. package/esm2022/lib/widget-library/checkboxes.component.mjs +147 -0
  34. package/esm2022/lib/widget-library/file.component.mjs +35 -0
  35. package/esm2022/lib/widget-library/hidden.component.mjs +54 -0
  36. package/esm2022/lib/widget-library/index.mjs +55 -0
  37. package/esm2022/lib/widget-library/input.component.mjs +119 -0
  38. package/esm2022/lib/widget-library/message.component.mjs +38 -0
  39. package/esm2022/lib/widget-library/none.component.mjs +21 -0
  40. package/esm2022/lib/widget-library/number.component.mjs +123 -0
  41. package/esm2022/lib/widget-library/one-of.component.mjs +35 -0
  42. package/esm2022/lib/widget-library/orderable.directive.mjs +123 -0
  43. package/esm2022/lib/widget-library/radios.component.mjs +153 -0
  44. package/esm2022/lib/widget-library/root.component.mjs +79 -0
  45. package/esm2022/lib/widget-library/section.component.mjs +199 -0
  46. package/esm2022/lib/widget-library/select-framework.component.mjs +51 -0
  47. package/esm2022/lib/widget-library/select-widget.component.mjs +46 -0
  48. package/esm2022/lib/widget-library/select.component.mjs +150 -0
  49. package/esm2022/lib/widget-library/submit.component.mjs +82 -0
  50. package/esm2022/lib/widget-library/tab.component.mjs +41 -0
  51. package/esm2022/lib/widget-library/tabs.component.mjs +108 -0
  52. package/esm2022/lib/widget-library/template.component.mjs +46 -0
  53. package/esm2022/lib/widget-library/textarea.component.mjs +104 -0
  54. package/esm2022/lib/widget-library/widget-library.module.mjs +42 -0
  55. package/esm2022/lib/widget-library/widget-library.service.mjs +226 -0
  56. package/esm2022/ng-formworks-core.mjs +5 -0
  57. package/esm2022/public_api.mjs +13 -0
  58. package/fesm2022/ng-formworks-core.mjs +10151 -0
  59. package/fesm2022/ng-formworks-core.mjs.map +1 -0
  60. package/index.d.ts +5 -0
  61. package/lib/framework-library/framework-library.service.d.ts +55 -0
  62. package/lib/framework-library/framework.d.ts +13 -0
  63. package/lib/framework-library/no-framework.component.d.ts +8 -0
  64. package/lib/framework-library/no-framework.module.d.ts +9 -0
  65. package/lib/framework-library/no.framework.d.ts +10 -0
  66. package/lib/json-schema-form.component.d.ts +218 -0
  67. package/lib/json-schema-form.module.d.ts +11 -0
  68. package/lib/json-schema-form.service.d.ts +115 -0
  69. package/lib/locale/de-validation-messages.d.ts +1 -0
  70. package/lib/locale/en-validation-messages.d.ts +1 -0
  71. package/lib/locale/es-validation-messages.d.ts +1 -0
  72. package/lib/locale/fr-validation-messages.d.ts +1 -0
  73. package/{src/lib/locale/index.ts → lib/locale/index.d.ts} +7 -7
  74. package/lib/locale/it-validation-messages.d.ts +1 -0
  75. package/lib/locale/pt-validation-messages.d.ts +1 -0
  76. package/lib/locale/zh-validation-messages.d.ts +1 -0
  77. package/lib/shared/convert-schema-to-draft6.function.d.ts +21 -0
  78. package/lib/shared/form-group.functions.d.ts +100 -0
  79. package/lib/shared/format-regex.constants.d.ts +19 -0
  80. package/lib/shared/index.d.ts +9 -0
  81. package/lib/shared/json-schema.functions.d.ts +193 -0
  82. package/lib/shared/json.validators.d.ts +441 -0
  83. package/lib/shared/jsonpointer.functions.d.ts +416 -0
  84. package/lib/shared/layout.functions.d.ts +83 -0
  85. package/lib/shared/merge-schemas.function.d.ts +19 -0
  86. package/lib/shared/utility.functions.d.ts +165 -0
  87. package/{src/lib/shared/validator.functions.ts → lib/shared/validator.functions.d.ts} +364 -601
  88. package/lib/widget-library/add-reference.component.d.ts +20 -0
  89. package/lib/widget-library/button.component.d.ts +21 -0
  90. package/lib/widget-library/checkbox.component.d.ts +24 -0
  91. package/lib/widget-library/checkboxes.component.d.ts +24 -0
  92. package/lib/widget-library/file.component.d.ts +21 -0
  93. package/lib/widget-library/hidden.component.d.ts +19 -0
  94. package/{src/lib/widget-library/index.ts → lib/widget-library/index.d.ts} +47 -56
  95. package/lib/widget-library/input.component.d.ts +22 -0
  96. package/lib/widget-library/message.component.d.ts +15 -0
  97. package/lib/widget-library/none.component.d.ts +8 -0
  98. package/lib/widget-library/number.component.d.ts +25 -0
  99. package/lib/widget-library/one-of.component.d.ts +21 -0
  100. package/lib/widget-library/orderable.directive.d.ts +41 -0
  101. package/lib/widget-library/radios.component.d.ts +23 -0
  102. package/lib/widget-library/root.component.d.ts +17 -0
  103. package/lib/widget-library/section.component.d.ts +19 -0
  104. package/lib/widget-library/select-framework.component.d.ts +18 -0
  105. package/lib/widget-library/select-widget.component.d.ts +18 -0
  106. package/lib/widget-library/select.component.d.ts +24 -0
  107. package/lib/widget-library/submit.component.d.ts +24 -0
  108. package/lib/widget-library/tab.component.d.ts +14 -0
  109. package/lib/widget-library/tabs.component.d.ts +20 -0
  110. package/lib/widget-library/template.component.d.ts +18 -0
  111. package/lib/widget-library/textarea.component.d.ts +21 -0
  112. package/lib/widget-library/widget-library.module.d.ts +31 -0
  113. package/lib/widget-library/widget-library.service.d.ts +22 -0
  114. package/package.json +66 -53
  115. package/{src/public_api.ts → public_api.d.ts} +9 -21
  116. package/karma.conf.js +0 -46
  117. package/ng-package.json +0 -11
  118. package/src/lib/framework-library/framework-library.service.ts +0 -195
  119. package/src/lib/framework-library/framework.ts +0 -11
  120. package/src/lib/framework-library/no-framework.component.html +0 -2
  121. package/src/lib/framework-library/no-framework.component.ts +0 -11
  122. package/src/lib/framework-library/no-framework.module.ts +0 -18
  123. package/src/lib/framework-library/no.framework.ts +0 -11
  124. package/src/lib/json-schema-form.component.html +0 -7
  125. package/src/lib/json-schema-form.component.ts +0 -809
  126. package/src/lib/json-schema-form.module.ts +0 -17
  127. package/src/lib/json-schema-form.service.ts +0 -907
  128. package/src/lib/locale/de-validation-messages.ts +0 -58
  129. package/src/lib/locale/en-validation-messages.ts +0 -58
  130. package/src/lib/locale/es-validation-messages.ts +0 -55
  131. package/src/lib/locale/fr-validation-messages.ts +0 -58
  132. package/src/lib/locale/it-validation-messages.ts +0 -58
  133. package/src/lib/locale/pt-validation-messages.ts +0 -58
  134. package/src/lib/locale/zh-validation-messages.ts +0 -58
  135. package/src/lib/locale-dates/en-US.ts +0 -5
  136. package/src/lib/shared/convert-schema-to-draft6.function.ts +0 -321
  137. package/src/lib/shared/form-group.functions.ts +0 -522
  138. package/src/lib/shared/format-regex.constants.ts +0 -73
  139. package/src/lib/shared/index.ts +0 -40
  140. package/src/lib/shared/json-schema.functions.ts +0 -788
  141. package/src/lib/shared/json.validators.ts +0 -878
  142. package/src/lib/shared/jsonpointer.functions.ts +0 -1012
  143. package/src/lib/shared/jspointer.functions.json.spec.ts +0 -103
  144. package/src/lib/shared/layout.functions.ts +0 -1233
  145. package/src/lib/shared/merge-schemas.function.ts +0 -329
  146. package/src/lib/shared/utility.functions.ts +0 -373
  147. package/src/lib/shared/validator.functions.spec.ts +0 -55
  148. package/src/lib/widget-library/add-reference.component.ts +0 -59
  149. package/src/lib/widget-library/button.component.ts +0 -54
  150. package/src/lib/widget-library/checkbox.component.ts +0 -74
  151. package/src/lib/widget-library/checkboxes.component.ts +0 -104
  152. package/src/lib/widget-library/file.component.ts +0 -36
  153. package/src/lib/widget-library/hidden.component.ts +0 -39
  154. package/src/lib/widget-library/input.component.ts +0 -76
  155. package/src/lib/widget-library/message.component.ts +0 -29
  156. package/src/lib/widget-library/none.component.ts +0 -12
  157. package/src/lib/widget-library/number.component.ts +0 -79
  158. package/src/lib/widget-library/one-of.component.ts +0 -36
  159. package/src/lib/widget-library/orderable.directive.ts +0 -130
  160. package/src/lib/widget-library/radios.component.ts +0 -101
  161. package/src/lib/widget-library/root.component.ts +0 -78
  162. package/src/lib/widget-library/section.component.ts +0 -133
  163. package/src/lib/widget-library/select-framework.component.ts +0 -50
  164. package/src/lib/widget-library/select-widget.component.ts +0 -46
  165. package/src/lib/widget-library/select.component.ts +0 -96
  166. package/src/lib/widget-library/submit.component.ts +0 -68
  167. package/src/lib/widget-library/tab.component.ts +0 -29
  168. package/src/lib/widget-library/tabs.component.ts +0 -83
  169. package/src/lib/widget-library/template.component.ts +0 -52
  170. package/src/lib/widget-library/textarea.component.ts +0 -68
  171. package/src/lib/widget-library/widget-library.module.ts +0 -13
  172. package/src/lib/widget-library/widget-library.service.ts +0 -234
  173. package/src/test.ts +0 -18
  174. package/tsconfig.lib.json +0 -25
  175. package/tsconfig.lib.prod.json +0 -9
  176. package/tsconfig.spec.json +0 -17
  177. package/tslint.json +0 -11
@@ -1,907 +0,0 @@
1
- import { Injectable, OnDestroy } from '@angular/core';
2
- import { AbstractControl, UntypedFormArray, UntypedFormGroup } from '@angular/forms';
3
- import Ajv, { ErrorObject, Options } from 'ajv';
4
- import jsonDraft6 from 'ajv/lib/refs/json-schema-draft-06.json';
5
- import cloneDeep from 'lodash/cloneDeep';
6
- import { Subject, Subscription } from 'rxjs';
7
- import {
8
- deValidationMessages,
9
- enValidationMessages,
10
- esValidationMessages,
11
- frValidationMessages,
12
- itValidationMessages,
13
- ptValidationMessages,
14
- zhValidationMessages
15
- } from './locale';
16
- import {
17
- JsonPointer,
18
- buildFormGroup,
19
- buildFormGroupTemplate,
20
- buildLayout,
21
- buildSchemaFromData,
22
- buildSchemaFromLayout,
23
- fixTitle,
24
- forEach,
25
- formatFormData,
26
- getControl,
27
- getLayoutNode,
28
- hasOwn,
29
- hasValue,
30
- isArray,
31
- isDefined,
32
- isEmpty,
33
- isObject,
34
- removeRecursiveReferences,
35
- toTitleCase
36
- } from './shared';
37
-
38
- import _isEqual from 'lodash/isEqual';
39
-
40
-
41
- export interface TitleMapItem {
42
- name?: string;
43
- value?: any;
44
- checked?: boolean;
45
- group?: string;
46
- items?: TitleMapItem[];
47
- }
48
- export interface ErrorMessages {
49
- [control_name: string]: {
50
- message: string | Function | Object;
51
- code: string;
52
- }[];
53
- }
54
-
55
- @Injectable()
56
- export class JsonSchemaFormService implements OnDestroy {
57
- JsonFormCompatibility = false;
58
- ReactJsonSchemaFormCompatibility = false;
59
- AngularSchemaFormCompatibility = false;
60
- tpldata: any = {};
61
-
62
- ajvOptions: Options = {
63
- allErrors: true,
64
- validateFormats:false,
65
- strict:false
66
-
67
- };
68
- ajv:any = new Ajv(this.ajvOptions); // AJV: Another JSON Schema Validator
69
-
70
- validateFormData: any = null; // Compiled AJV function to validate active form's schema
71
-
72
- formValues: any = {}; // Internal form data (may not have correct types)
73
- data: any = {}; // Output form data (formValues, formatted with correct data types)
74
- schema: any = {}; // Internal JSON Schema
75
- layout: any[] = []; // Internal form layout
76
- formGroupTemplate: any = {}; // Template used to create formGroup
77
- formGroup: any = null; // Angular formGroup, which powers the reactive form
78
- framework: any = null; // Active framework component
79
- formOptions: any; // Active options, used to configure the form
80
-
81
- validData: any = null; // Valid form data (or null) (=== isValid ? data : null)
82
- isValid: boolean = null; // Is current form data valid?
83
- ajvErrors: ErrorObject[] = null; // Ajv errors for current data
84
- validationErrors: any = null; // Any validation errors for current data
85
- dataErrors: any = new Map(); //
86
- formValueSubscription: any = null; // Subscription to formGroup.valueChanges observable (for un- and re-subscribing)
87
- dataChanges: Subject<any> = new Subject(); // Form data observable
88
- isValidChanges: Subject<any> = new Subject(); // isValid observable
89
- validationErrorChanges: Subject<any> = new Subject(); // validationErrors observable
90
-
91
- arrayMap: Map<string, number> = new Map(); // Maps arrays in data object and number of tuple values
92
- dataMap: Map<string, any> = new Map(); // Maps paths in form data to schema and formGroup paths
93
- dataRecursiveRefMap: Map<string, string> = new Map(); // Maps recursive reference points in form data
94
- schemaRecursiveRefMap: Map<string, string> = new Map(); // Maps recursive reference points in schema
95
- schemaRefLibrary: any = {}; // Library of schemas for resolving schema $refs
96
- layoutRefLibrary: any = { '': null }; // Library of layout nodes for adding to form
97
- templateRefLibrary: any = {}; // Library of formGroup templates for adding to form
98
- hasRootReference = false; // Does the form include a recursive reference to itself?
99
-
100
- language = 'en-US'; // Does the form include a recursive reference to itself?
101
-
102
- // Default global form options
103
- defaultFormOptions: any = {
104
- autocomplete: true, // Allow the web browser to remember previous form submission values as defaults
105
- addSubmit: 'auto', // Add a submit button if layout does not have one?
106
- // for addSubmit: true = always, false = never,
107
- // 'auto' = only if layout is undefined (form is built from schema alone)
108
- debug: false, // Show debugging output?
109
- disableInvalidSubmit: true, // Disable submit if form invalid?
110
- formDisabled: false, // Set entire form as disabled? (not editable, and disables outputs)
111
- formReadonly: false, // Set entire form as read only? (not editable, but outputs still enabled)
112
- fieldsRequired: false, // (set automatically) Are there any required fields in the form?
113
- framework: 'no-framework', // The framework to load
114
- loadExternalAssets: false, // Load external css and JavaScript for framework?
115
- pristine: { errors: true, success: true },
116
- supressPropertyTitles: false,
117
- setSchemaDefaults: 'auto', // Set fefault values from schema?
118
- // true = always set (unless overridden by layout default or formValues)
119
- // false = never set
120
- // 'auto' = set in addable components, and everywhere if formValues not set
121
- setLayoutDefaults: 'auto', // Set fefault values from layout?
122
- // true = always set (unless overridden by formValues)
123
- // false = never set
124
- // 'auto' = set in addable components, and everywhere if formValues not set
125
- validateOnRender: 'auto', // Validate fields immediately, before they are touched?
126
- // true = validate all fields immediately
127
- // false = only validate fields after they are touched by user
128
- // 'auto' = validate fields with values immediately, empty fields after they are touched
129
- widgets: {}, // Any custom widgets to load
130
- defaultWidgetOptions: {
131
- // Default options for form control widgets
132
- listItems: 1, // Number of list items to initially add to arrays with no default value
133
- addable: true, // Allow adding items to an array or $ref point?
134
- orderable: true, // Allow reordering items within an array?
135
- removable: true, // Allow removing items from an array or $ref point?
136
- enableErrorState: true, // Apply 'has-error' class when field fails validation?
137
- // disableErrorState: false, // Don't apply 'has-error' class when field fails validation?
138
- enableSuccessState: true, // Apply 'has-success' class when field validates?
139
- // disableSuccessState: false, // Don't apply 'has-success' class when field validates?
140
- feedback: false, // Show inline feedback icons?
141
- feedbackOnRender: false, // Show errorMessage on Render?
142
- notitle: false, // Hide title?
143
- disabled: false, // Set control as disabled? (not editable, and excluded from output)
144
- readonly: false, // Set control as read only? (not editable, but included in output)
145
- returnEmptyFields: true, // return values for fields that contain no data?
146
- validationMessages: {} // set by setLanguage()
147
- }
148
- };
149
-
150
- fcValueChangesSubs:Subscription;
151
- fcStatusChangesSubs:Subscription;
152
- constructor() {
153
- this.setLanguage(this.language);
154
- this.ajv.addMetaSchema(jsonDraft6);
155
- }
156
- ngOnDestroy(): void {
157
- this.fcValueChangesSubs?.unsubscribe();
158
- this.fcStatusChangesSubs?.unsubscribe();
159
- this.formValueSubscription?.unsubscribe();
160
- this.fcValueChangesSubs=null;
161
- this.fcStatusChangesSubs=null;
162
- this.formValueSubscription=null;
163
-
164
- }
165
-
166
- setLanguage(language: string = 'en-US') {
167
- this.language = language;
168
- const languageValidationMessages = {
169
- de: deValidationMessages,
170
- en: enValidationMessages,
171
- es: esValidationMessages,
172
- fr: frValidationMessages,
173
- it: itValidationMessages,
174
- pt: ptValidationMessages,
175
- zh: zhValidationMessages,
176
- };
177
- const languageCode = language.slice(0, 2);
178
-
179
- const validationMessages = languageValidationMessages[languageCode];
180
-
181
- this.defaultFormOptions.defaultWidgetOptions.validationMessages = cloneDeep(
182
- validationMessages
183
- );
184
- }
185
-
186
- getData() {
187
- return this.data;
188
- }
189
-
190
- getSchema() {
191
- return this.schema;
192
- }
193
-
194
- getLayout() {
195
- return this.layout;
196
- }
197
-
198
- resetAllValues() {
199
- this.JsonFormCompatibility = false;
200
- this.ReactJsonSchemaFormCompatibility = false;
201
- this.AngularSchemaFormCompatibility = false;
202
- this.tpldata = {};
203
- this.validateFormData = null;
204
- this.formValues = {};
205
- this.schema = {};
206
- this.layout = [];
207
- this.formGroupTemplate = {};
208
- this.formGroup = null;
209
- this.framework = null;
210
- this.data = {};
211
- this.validData = null;
212
- this.isValid = null;
213
- this.validationErrors = null;
214
- this.arrayMap = new Map();
215
- this.dataMap = new Map();
216
- this.dataRecursiveRefMap = new Map();
217
- this.schemaRecursiveRefMap = new Map();
218
- this.layoutRefLibrary = {};
219
- this.schemaRefLibrary = {};
220
- this.templateRefLibrary = {};
221
- this.formOptions = cloneDeep(this.defaultFormOptions);
222
- }
223
-
224
- /**
225
- * 'buildRemoteError' function
226
- *
227
- * Example errors:
228
- * {
229
- * last_name: [ {
230
- * message: 'Last name must by start with capital letter.',
231
- * code: 'capital_letter'
232
- * } ],
233
- * email: [ {
234
- * message: 'Email must be from example.com domain.',
235
- * code: 'special_domain'
236
- * }, {
237
- * message: 'Email must contain an @ symbol.',
238
- * code: 'at_symbol'
239
- * } ]
240
- * }
241
- * //{ErrorMessages} errors
242
- */
243
- buildRemoteError(errors: ErrorMessages) {
244
- forEach(errors, (value, key) => {
245
- if (key in this.formGroup.controls) {
246
- for (const error of value) {
247
- const err = {};
248
- err[error['code']] = error['message'];
249
- this.formGroup.get(key).setErrors(err, { emitEvent: true });
250
- }
251
- }
252
- });
253
- }
254
-
255
- validateData(newValue: any, updateSubscriptions = true): void {
256
- // Format raw form data to correct data types
257
- this.data = formatFormData(
258
- newValue,
259
- this.dataMap,
260
- this.dataRecursiveRefMap,
261
- this.arrayMap,
262
- this.formOptions.returnEmptyFields
263
- );
264
- this.isValid = this.validateFormData(this.data);
265
- this.validData = this.isValid ? this.data : null;
266
- const compileErrors = (errors:ErrorObject[]) => {
267
- const compiledErrors = {};
268
- (errors || []).forEach(error => {
269
- //TODO review-seems to be a change in newer versions
270
- //of ajv giving '' as instancePath for root objects
271
- let errorPath=error.instancePath||"ROOT";
272
- if (!compiledErrors[errorPath]) {
273
- compiledErrors[errorPath] = [];
274
- }
275
- compiledErrors[errorPath].push(error.message);
276
- });
277
- return compiledErrors;
278
- };
279
- this.ajvErrors = this.validateFormData.errors;
280
- this.validationErrors = compileErrors(this.validateFormData.errors);
281
- if (updateSubscriptions) {
282
- this.dataChanges.next(this.data);
283
- this.isValidChanges.next(this.isValid);
284
- this.validationErrorChanges.next(this.ajvErrors);
285
- }
286
- }
287
-
288
- buildFormGroupTemplate(formValues: any = null, setValues = true) {
289
- this.formGroupTemplate = buildFormGroupTemplate(
290
- this,
291
- formValues,
292
- setValues
293
- );
294
- }
295
-
296
- buildFormGroup() {
297
- this.formGroup = <UntypedFormGroup>buildFormGroup(this.formGroupTemplate);
298
- if (this.formGroup) {
299
- this.compileAjvSchema();
300
- this.validateData(this.formGroup.value);
301
-
302
- // Set up observables to emit data and validation info when form data changes
303
- if (this.formValueSubscription) {
304
- this.formValueSubscription.unsubscribe();
305
- }
306
- this.formValueSubscription = this.formGroup.valueChanges.subscribe(
307
- formValue => this.validateData(formValue)
308
- );
309
- }
310
- }
311
-
312
- buildLayout(widgetLibrary: any) {
313
- this.layout = buildLayout(this, widgetLibrary);
314
- }
315
-
316
- setOptions(newOptions: any) {
317
- if (isObject(newOptions)) {
318
- const addOptions = cloneDeep(newOptions);
319
- // Backward compatibility for 'defaultOptions' (renamed 'defaultWidgetOptions')
320
- if (isObject(addOptions.defaultOptions)) {
321
- Object.assign(
322
- this.formOptions.defaultWidgetOptions,
323
- addOptions.defaultOptions
324
- );
325
- delete addOptions.defaultOptions;
326
- }
327
- if (isObject(addOptions.defaultWidgetOptions)) {
328
- Object.assign(
329
- this.formOptions.defaultWidgetOptions,
330
- addOptions.defaultWidgetOptions
331
- );
332
- delete addOptions.defaultWidgetOptions;
333
- }
334
- Object.assign(this.formOptions, addOptions);
335
-
336
- // convert disableErrorState / disableSuccessState to enable...
337
- const globalDefaults = this.formOptions.defaultWidgetOptions;
338
- ['ErrorState', 'SuccessState']
339
- .filter(suffix => hasOwn(globalDefaults, 'disable' + suffix))
340
- .forEach(suffix => {
341
- globalDefaults['enable' + suffix] = !globalDefaults[
342
- 'disable' + suffix
343
- ];
344
- delete globalDefaults['disable' + suffix];
345
- });
346
- }
347
- }
348
-
349
- compileAjvSchema() {
350
- if (!this.validateFormData) {
351
- // if 'ui:order' exists in properties, move it to root before compiling with ajv
352
- if (Array.isArray(this.schema.properties['ui:order'])) {
353
- this.schema['ui:order'] = this.schema.properties['ui:order'];
354
- delete this.schema.properties['ui:order'];
355
- }
356
- this.ajv.removeSchema(this.schema);
357
- this.validateFormData = this.ajv.compile(this.schema);
358
- }
359
- }
360
-
361
- buildSchemaFromData(data?: any, requireAllFields = false): any {
362
- if (data) {
363
- return buildSchemaFromData(data, requireAllFields);
364
- }
365
- this.schema = buildSchemaFromData(this.formValues, requireAllFields);
366
- }
367
-
368
- buildSchemaFromLayout(layout?: any): any {
369
- if (layout) {
370
- return buildSchemaFromLayout(layout);
371
- }
372
- this.schema = buildSchemaFromLayout(this.layout);
373
- }
374
-
375
- setTpldata(newTpldata: any = {}): void {
376
- this.tpldata = newTpldata;
377
- }
378
-
379
- parseText(
380
- text = '',
381
- value: any = {},
382
- values: any = {},
383
- key: number | string = null
384
- ): string {
385
- if (!text || !/{{.+?}}/.test(text)) {
386
- return text;
387
- }
388
- return text.replace(/{{(.+?)}}/g, (...a) =>
389
- this.parseExpression(a[1], value, values, key, this.tpldata)
390
- );
391
- }
392
-
393
- parseExpression(
394
- expression = '',
395
- value: any = {},
396
- values: any = {},
397
- key: number | string = null,
398
- tpldata: any = null
399
- ) {
400
- if (typeof expression !== 'string') {
401
- return '';
402
- }
403
- const index = typeof key === 'number' ? key + 1 + '' : key || '';
404
- expression = expression.trim();
405
- if (
406
- (expression[0] === "'" || expression[0] === '"') &&
407
- expression[0] === expression[expression.length - 1] &&
408
- expression.slice(1, expression.length - 1).indexOf(expression[0]) === -1
409
- ) {
410
- return expression.slice(1, expression.length - 1);
411
- }
412
- if (expression === 'idx' || expression === '$index') {
413
- return index;
414
- }
415
- if (expression === 'value' && !hasOwn(values, 'value')) {
416
- return value;
417
- }
418
- if (
419
- ['"', "'", ' ', '||', '&&', '+'].every(
420
- delim => expression.indexOf(delim) === -1
421
- )
422
- ) {
423
- const pointer = JsonPointer.parseObjectPath(expression);
424
- return pointer[0] === 'value' && JsonPointer.has(value, pointer.slice(1))
425
- ? JsonPointer.get(value, pointer.slice(1))
426
- : pointer[0] === 'values' && JsonPointer.has(values, pointer.slice(1))
427
- ? JsonPointer.get(values, pointer.slice(1))
428
- : pointer[0] === 'tpldata' && JsonPointer.has(tpldata, pointer.slice(1))
429
- ? JsonPointer.get(tpldata, pointer.slice(1))
430
- : JsonPointer.has(values, pointer)
431
- ? JsonPointer.get(values, pointer)
432
- : '';
433
- }
434
- if (expression.indexOf('[idx]') > -1) {
435
- expression = expression.replace(/\[idx\]/g, <string>index);
436
- }
437
- if (expression.indexOf('[$index]') > -1) {
438
- expression = expression.replace(/\[$index\]/g, <string>index);
439
- }
440
- // TODO: Improve expression evaluation by parsing quoted strings first
441
- // let expressionArray = expression.match(/([^"']+|"[^"]+"|'[^']+')/g);
442
- if (expression.indexOf('||') > -1) {
443
- return expression
444
- .split('||')
445
- .reduce(
446
- (all, term) =>
447
- all || this.parseExpression(term, value, values, key, tpldata),
448
- ''
449
- );
450
- }
451
- if (expression.indexOf('&&') > -1) {
452
- return expression
453
- .split('&&')
454
- .reduce(
455
- (all, term) =>
456
- all && this.parseExpression(term, value, values, key, tpldata),
457
- ' '
458
- )
459
- .trim();
460
- }
461
- if (expression.indexOf('+') > -1) {
462
- return expression
463
- .split('+')
464
- .map(term => this.parseExpression(term, value, values, key, tpldata))
465
- .join('');
466
- }
467
- return '';
468
- }
469
-
470
- setArrayItemTitle(
471
- parentCtx: any = {},
472
- childNode: any = null,
473
- index: number = null
474
- ): string {
475
- const parentNode = parentCtx.layoutNode;
476
- const parentValues: any = this.getFormControlValue(parentCtx);
477
- const isArrayItem =
478
- (parentNode.type || '').slice(-5) === 'array' && isArray(parentValues);
479
- const text = JsonPointer.getFirst(
480
- isArrayItem && childNode.type !== '$ref'
481
- ? [
482
- [childNode, '/options/legend'],
483
- [childNode, '/options/title'],
484
- [parentNode, '/options/title'],
485
- [parentNode, '/options/legend']
486
- ]
487
- : [
488
- [childNode, '/options/title'],
489
- [childNode, '/options/legend'],
490
- [parentNode, '/options/title'],
491
- [parentNode, '/options/legend']
492
- ]
493
- );
494
- if (!text) {
495
- return text;
496
- }
497
- const childValue =
498
- isArray(parentValues) && index < parentValues.length
499
- ? parentValues[index]
500
- : parentValues;
501
- return this.parseText(text, childValue, parentValues, index);
502
- }
503
-
504
- setItemTitle(ctx: any) {
505
- return !ctx.options.title && /^(\d+|-)$/.test(ctx.layoutNode.name)
506
- ? null
507
- : this.parseText(
508
- ctx.options.title || toTitleCase(ctx.layoutNode.name),
509
- this.getFormControlValue(this),
510
- (this.getFormControlGroup(this) || <any>{}).value,
511
- ctx.dataIndex[ctx.dataIndex.length - 1]
512
- );
513
- }
514
-
515
- evaluateCondition(layoutNode: any, dataIndex: number[]): boolean {
516
- const arrayIndex = dataIndex && dataIndex[dataIndex.length - 1];
517
- let result = true;
518
- if (hasValue((layoutNode.options || {}).condition)) {
519
- if (typeof layoutNode.options.condition === 'string') {
520
- let pointer = layoutNode.options.condition;
521
- if (hasValue(arrayIndex)) {
522
- pointer = pointer.replace('[arrayIndex]', `[${arrayIndex}]`);
523
- }
524
- pointer = JsonPointer.parseObjectPath(pointer);
525
- result = !!JsonPointer.get(this.data, pointer);
526
- if (!result && pointer[0] === 'model') {
527
- result = !!JsonPointer.get({ model: this.data }, pointer);
528
- }
529
- } else if (typeof layoutNode.options.condition === 'function') {
530
- result = layoutNode.options.condition(this.data);
531
- } else if (
532
- typeof layoutNode.options.condition.functionBody === 'string'
533
- ) {
534
- try {
535
- const dynFn = new Function(
536
- 'model',
537
- 'arrayIndices',
538
- layoutNode.options.condition.functionBody
539
- );
540
- result = dynFn(this.data, dataIndex);
541
- } catch (e) {
542
- result = true;
543
- console.error(
544
- 'condition functionBody errored out on evaluation: ' +
545
- layoutNode.options.condition.functionBody
546
- );
547
- }
548
- }
549
- }
550
- return result;
551
- }
552
-
553
- initializeControl(ctx: any, bind = true): boolean {
554
- if (!isObject(ctx)) {
555
- return false;
556
- }
557
- if (isEmpty(ctx.options)) {
558
- ctx.options = !isEmpty((ctx.layoutNode || {}).options)
559
- ? ctx.layoutNode.options
560
- : cloneDeep(this.formOptions);
561
- }
562
- ctx.formControl = this.getFormControl(ctx);
563
- ctx.boundControl = bind && !!ctx.formControl;
564
- if (ctx.formControl) {
565
- ctx.controlName = this.getFormControlName(ctx);
566
- ctx.controlValue = ctx.formControl.value;
567
- ctx.controlDisabled = ctx.formControl.disabled;
568
- ctx.options.errorMessage =
569
- ctx.formControl.status === 'VALID'
570
- ? null
571
- : this.formatErrors(
572
- ctx.formControl.errors,
573
- ctx.options.validationMessages
574
- );
575
- ctx.options.showErrors =
576
- this.formOptions.validateOnRender === true ||
577
- (this.formOptions.validateOnRender === 'auto' &&
578
- hasValue(ctx.controlValue));
579
- this.fcStatusChangesSubs=ctx.formControl.statusChanges.subscribe(
580
- status =>
581
- (ctx.options.errorMessage =
582
- status === 'VALID'
583
- ? null
584
- : this.formatErrors(
585
- ctx.formControl.errors,
586
- ctx.options.validationMessages
587
- ))
588
- );
589
- this.fcValueChangesSubs=ctx.formControl.valueChanges.subscribe(value => {
590
- //commented out to revert back to previous commits
591
- //as seems to be causing some issues
592
- /*
593
- if (!!value) {
594
- ctx.controlValue = value;
595
- }
596
- */
597
- //TODO-test,this is the original code
598
- if (!_isEqual(ctx.controlValue, value)) { ctx.controlValue = value }
599
- });
600
- } else {
601
- ctx.controlName = ctx.layoutNode.name;
602
- ctx.controlValue = ctx.layoutNode.value || null;
603
- const dataPointer = this.getDataPointer(ctx);
604
- if (bind && dataPointer) {
605
- console.error(
606
- `warning: control "${dataPointer}" is not bound to the Angular FormGroup.`
607
- );
608
- }
609
- }
610
- return ctx.boundControl;
611
- }
612
-
613
- formatErrors(errors: any, validationMessages: any = {}): string {
614
- if (isEmpty(errors)) {
615
- return null;
616
- }
617
- if (!isObject(validationMessages)) {
618
- validationMessages = {};
619
- }
620
- const addSpaces = string =>
621
- string[0].toUpperCase() +
622
- (string.slice(1) || '')
623
- .replace(/([a-z])([A-Z])/g, '$1 $2')
624
- .replace(/_/g, ' ');
625
- const formatError = error =>
626
- typeof error === 'object'
627
- ? Object.keys(error)
628
- .map(key =>
629
- error[key] === true
630
- ? addSpaces(key)
631
- : error[key] === false
632
- ? 'Not ' + addSpaces(key)
633
- : addSpaces(key) + ': ' + formatError(error[key])
634
- )
635
- .join(', ')
636
- : addSpaces(error.toString());
637
- const messages = [];
638
- return (
639
- Object.keys(errors)
640
- // Hide 'required' error, unless it is the only one
641
- .filter(
642
- errorKey =>
643
- errorKey !== 'required' || Object.keys(errors).length === 1
644
- )
645
- .map(errorKey =>
646
- // If validationMessages is a string, return it
647
- typeof validationMessages === 'string'
648
- ? validationMessages
649
- : // If custom error message is a function, return function result
650
- typeof validationMessages[errorKey] === 'function'
651
- ? validationMessages[errorKey](errors[errorKey])
652
- : // If custom error message is a string, replace placeholders and return
653
- typeof validationMessages[errorKey] === 'string'
654
- ? // Does error message have any {{property}} placeholders?
655
- !/{{.+?}}/.test(validationMessages[errorKey])
656
- ? validationMessages[errorKey]
657
- : // Replace {{property}} placeholders with values
658
- Object.keys(errors[errorKey]).reduce(
659
- (errorMessage, errorProperty) =>
660
- errorMessage.replace(
661
- new RegExp('{{' + errorProperty + '}}', 'g'),
662
- errors[errorKey][errorProperty]
663
- ),
664
- validationMessages[errorKey]
665
- )
666
- : // If no custom error message, return formatted error data instead
667
- addSpaces(errorKey) + ' Error: ' + formatError(errors[errorKey])
668
- )
669
- .join('<br>')
670
- );
671
- }
672
-
673
- updateValue(ctx: any, value: any): void {
674
- // Set value of current control
675
- ctx.controlValue = value;
676
- if (ctx.boundControl) {
677
- ctx.formControl.setValue(value);
678
- ctx.formControl.markAsDirty();
679
- }
680
- ctx.layoutNode.value = value;
681
-
682
- // Set values of any related controls in copyValueTo array
683
- if (isArray(ctx.options.copyValueTo)) {
684
- for (const item of ctx.options.copyValueTo) {
685
- const targetControl = getControl(this.formGroup, item);
686
- if (
687
- isObject(targetControl) &&
688
- typeof targetControl.setValue === 'function'
689
- ) {
690
- targetControl.setValue(value);
691
- targetControl.markAsDirty();
692
- }
693
- }
694
- }
695
- }
696
-
697
- updateArrayCheckboxList(ctx: any, checkboxList: TitleMapItem[]): void {
698
- const formArray = <UntypedFormArray>this.getFormControl(ctx);
699
-
700
- // Remove all existing items
701
- while (formArray.value.length) {
702
- formArray.removeAt(0);
703
- }
704
-
705
- // Re-add an item for each checked box
706
- const refPointer = removeRecursiveReferences(
707
- ctx.layoutNode.dataPointer + '/-',
708
- this.dataRecursiveRefMap,
709
- this.arrayMap
710
- );
711
- for (const checkboxItem of checkboxList) {
712
- if (checkboxItem.checked) {
713
- const newFormControl = buildFormGroup(
714
- this.templateRefLibrary[refPointer]
715
- );
716
- newFormControl.setValue(checkboxItem.value);
717
- formArray.push(newFormControl);
718
- }
719
- }
720
- formArray.markAsDirty();
721
- }
722
-
723
- getFormControl(ctx: any): AbstractControl {
724
- if (
725
- !ctx.layoutNode ||
726
- !isDefined(ctx.layoutNode.dataPointer) ||
727
- ctx.layoutNode.type === '$ref'
728
- ) {
729
- return null;
730
- }
731
- return getControl(this.formGroup, this.getDataPointer(ctx));
732
- }
733
-
734
- getFormControlValue(ctx: any): AbstractControl {
735
- if (
736
- !ctx.layoutNode ||
737
- !isDefined(ctx.layoutNode.dataPointer) ||
738
- ctx.layoutNode.type === '$ref'
739
- ) {
740
- return null;
741
- }
742
- const control = getControl(this.formGroup, this.getDataPointer(ctx));
743
- return control ? control.value : null;
744
- }
745
-
746
- getFormControlGroup(ctx: any): UntypedFormArray | UntypedFormGroup {
747
- if (!ctx.layoutNode || !isDefined(ctx.layoutNode.dataPointer)) {
748
- return null;
749
- }
750
- return getControl(this.formGroup, this.getDataPointer(ctx), true);
751
- }
752
-
753
- getFormControlName(ctx: any): string {
754
- if (
755
- !ctx.layoutNode ||
756
- !isDefined(ctx.layoutNode.dataPointer) ||
757
- !hasValue(ctx.dataIndex)
758
- ) {
759
- return null;
760
- }
761
- return JsonPointer.toKey(this.getDataPointer(ctx));
762
- }
763
-
764
- getLayoutArray(ctx: any): any[] {
765
- return JsonPointer.get(this.layout, this.getLayoutPointer(ctx), 0, -1);
766
- }
767
-
768
- getParentNode(ctx: any): any {
769
- return JsonPointer.get(this.layout, this.getLayoutPointer(ctx), 0, -2);
770
- }
771
-
772
- getDataPointer(ctx: any): string {
773
- if (
774
- !ctx.layoutNode ||
775
- !isDefined(ctx.layoutNode.dataPointer) ||
776
- !hasValue(ctx.dataIndex)
777
- ) {
778
- return null;
779
- }
780
- return JsonPointer.toIndexedPointer(
781
- ctx.layoutNode.dataPointer,
782
- ctx.dataIndex,
783
- this.arrayMap
784
- );
785
- }
786
-
787
- getLayoutPointer(ctx: any): string {
788
- if (!hasValue(ctx.layoutIndex)) {
789
- return null;
790
- }
791
- return '/' + ctx.layoutIndex.join('/items/');
792
- }
793
-
794
- isControlBound(ctx: any): boolean {
795
- if (
796
- !ctx.layoutNode ||
797
- !isDefined(ctx.layoutNode.dataPointer) ||
798
- !hasValue(ctx.dataIndex)
799
- ) {
800
- return false;
801
- }
802
- const controlGroup = this.getFormControlGroup(ctx);
803
- const name = this.getFormControlName(ctx);
804
- return controlGroup ? hasOwn(controlGroup.controls, name) : false;
805
- }
806
-
807
- addItem(ctx: any, name?: string): boolean {
808
- if (
809
- !ctx.layoutNode ||
810
- !isDefined(ctx.layoutNode.$ref) ||
811
- !hasValue(ctx.dataIndex) ||
812
- !hasValue(ctx.layoutIndex)
813
- ) {
814
- return false;
815
- }
816
-
817
- // Create a new Angular form control from a template in templateRefLibrary
818
- const newFormGroup = buildFormGroup(
819
- this.templateRefLibrary[ctx.layoutNode.$ref]
820
- );
821
-
822
- // Add the new form control to the parent formArray or formGroup
823
- if (ctx.layoutNode.arrayItem) {
824
- // Add new array item to formArray
825
- (<UntypedFormArray>this.getFormControlGroup(ctx)).push(newFormGroup);
826
- } else {
827
- // Add new $ref item to formGroup
828
- (<UntypedFormGroup>this.getFormControlGroup(ctx)).addControl(
829
- name || this.getFormControlName(ctx),
830
- newFormGroup
831
- );
832
- }
833
-
834
- // Copy a new layoutNode from layoutRefLibrary
835
- const newLayoutNode = getLayoutNode(ctx.layoutNode, this);
836
- newLayoutNode.arrayItem = ctx.layoutNode.arrayItem;
837
- if (ctx.layoutNode.arrayItemType) {
838
- newLayoutNode.arrayItemType = ctx.layoutNode.arrayItemType;
839
- } else {
840
- delete newLayoutNode.arrayItemType;
841
- }
842
- if (name) {
843
- newLayoutNode.name = name;
844
- newLayoutNode.dataPointer += '/' + JsonPointer.escape(name);
845
- newLayoutNode.options.title = fixTitle(name);
846
- }
847
-
848
- // Add the new layoutNode to the form layout
849
- JsonPointer.insert(this.layout, this.getLayoutPointer(ctx), newLayoutNode);
850
-
851
- return true;
852
- }
853
-
854
- moveArrayItem(ctx: any, oldIndex: number, newIndex: number): boolean {
855
- if (
856
- !ctx.layoutNode ||
857
- !isDefined(ctx.layoutNode.dataPointer) ||
858
- !hasValue(ctx.dataIndex) ||
859
- !hasValue(ctx.layoutIndex) ||
860
- !isDefined(oldIndex) ||
861
- !isDefined(newIndex) ||
862
- oldIndex === newIndex
863
- ) {
864
- return false;
865
- }
866
-
867
- // Move item in the formArray
868
- const formArray = <UntypedFormArray>this.getFormControlGroup(ctx);
869
- const arrayItem = formArray.at(oldIndex);
870
- formArray.removeAt(oldIndex);
871
- formArray.insert(newIndex, arrayItem);
872
- formArray.updateValueAndValidity();
873
-
874
- // Move layout item
875
- const layoutArray = this.getLayoutArray(ctx);
876
- layoutArray.splice(newIndex, 0, layoutArray.splice(oldIndex, 1)[0]);
877
- return true;
878
- }
879
-
880
- removeItem(ctx: any): boolean {
881
- if (
882
- !ctx.layoutNode ||
883
- !isDefined(ctx.layoutNode.dataPointer) ||
884
- !hasValue(ctx.dataIndex) ||
885
- !hasValue(ctx.layoutIndex)
886
- ) {
887
- return false;
888
- }
889
-
890
- // Remove the Angular form control from the parent formArray or formGroup
891
- if (ctx.layoutNode.arrayItem) {
892
- // Remove array item from formArray
893
- (<UntypedFormArray>this.getFormControlGroup(ctx)).removeAt(
894
- ctx.dataIndex[ctx.dataIndex.length - 1]
895
- );
896
- } else {
897
- // Remove $ref item from formGroup
898
- (<UntypedFormGroup>this.getFormControlGroup(ctx)).removeControl(
899
- this.getFormControlName(ctx)
900
- );
901
- }
902
-
903
- // Remove layoutNode from layout
904
- JsonPointer.remove(this.layout, this.getLayoutPointer(ctx));
905
- return true;
906
- }
907
- }