@ng-formworks/core 17.2.7 → 17.4.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 +836 -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 +679 -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 +1154 -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 +10149 -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 +64 -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,809 +0,0 @@
1
- import cloneDeep from 'lodash/cloneDeep';
2
- import isEqual from 'lodash/isEqual';
3
-
4
- import {
5
- ChangeDetectionStrategy,
6
- ChangeDetectorRef,
7
- Component,
8
- EventEmitter,
9
- forwardRef,
10
- Input,
11
- OnChanges,
12
- OnDestroy,
13
- OnInit,
14
- Output,
15
- SimpleChanges,
16
- } from '@angular/core';
17
- import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
18
- import { Subject, Subscription } from 'rxjs';
19
- import { takeUntil } from 'rxjs/operators';
20
- import { FrameworkLibraryService } from './framework-library/framework-library.service';
21
- import { JsonSchemaFormService } from './json-schema-form.service';
22
- import { convertSchemaToDraft6 } from './shared/convert-schema-to-draft6.function';
23
- import { resolveSchemaReferences } from './shared/json-schema.functions';
24
- import { JsonPointer } from './shared/jsonpointer.functions';
25
- import { forEach, hasOwn } from './shared/utility.functions';
26
- import {
27
- hasValue,
28
- inArray,
29
- isArray,
30
- isEmpty,
31
- isObject
32
- } from './shared/validator.functions';
33
- import { WidgetLibraryService } from './widget-library/widget-library.service';
34
-
35
- export const JSON_SCHEMA_FORM_VALUE_ACCESSOR: any = {
36
- provide: NG_VALUE_ACCESSOR,
37
- useExisting: forwardRef(() => JsonSchemaFormComponent),
38
- multi: true,
39
- };
40
-
41
- /**
42
- * @module 'JsonSchemaFormComponent' - Angular JSON Schema Form
43
- *
44
- * Root module of the Angular JSON Schema Form client-side library,
45
- * an Angular library which generates an HTML form from a JSON schema
46
- * structured data model and/or a JSON Schema Form layout description.
47
- *
48
- * This library also validates input data by the user, using both validators on
49
- * individual controls to provide real-time feedback while the user is filling
50
- * out the form, and then validating the entire input against the schema when
51
- * the form is submitted to make sure the returned JSON data object is valid.
52
- *
53
- * This library is similar to, and mostly API compatible with:
54
- *
55
- * - JSON Schema Form's Angular Schema Form library for AngularJs
56
- * http://schemaform.io
57
- * http://schemaform.io/examples/bootstrap-example.html (examples)
58
- *
59
- * - Mozilla's react-jsonschema-form library for React
60
- * https://github.com/mozilla-services/react-jsonschema-form
61
- * https://mozilla-services.github.io/react-jsonschema-form (examples)
62
- *
63
- * - Joshfire's JSON Form library for jQuery
64
- * https://github.com/joshfire/jsonform
65
- * http://ulion.github.io/jsonform/playground (examples)
66
- *
67
- * This library depends on:
68
- * - Angular (obviously) https://angular.io
69
- * - lodash, JavaScript utility library https://github.com/lodash/lodash
70
- * - ajv, Another JSON Schema validator https://github.com/epoberezkin/ajv
71
- *
72
- * In addition, the Example Playground also depends on:
73
- * - brace, Browserified Ace editor http://thlorenz.github.io/brace
74
- */
75
- @Component({
76
- // tslint:disable-next-line:component-selector
77
- selector: 'json-schema-form',
78
- templateUrl: './json-schema-form.component.html',
79
- changeDetection: ChangeDetectionStrategy.OnPush,
80
- // Adding 'JsonSchemaFormService' here, instead of in the module,
81
- // creates a separate instance of the service for each component
82
- providers: [ JsonSchemaFormService, JSON_SCHEMA_FORM_VALUE_ACCESSOR ],
83
- })
84
- export class JsonSchemaFormComponent implements ControlValueAccessor, OnChanges, OnInit,OnDestroy {
85
- // TODO: quickfix to avoid subscribing twice to the same emitters
86
- private unsubscribeOnActivateForm$ = new Subject<void>();
87
-
88
- debugOutput: any; // Debug information, if requested
89
- formValueSubscription: any = null;
90
- formInitialized = false;
91
- objectWrap = false; // Is non-object input schema wrapped in an object?
92
-
93
- formValuesInput: string; // Name of the input providing the form data
94
- previousInputs: { // Previous input values, to detect which input triggers onChanges
95
- schema: any, layout: any[], data: any, options: any, framework: any | string,
96
- widgets: any, form: any, model: any, JSONSchema: any, UISchema: any,
97
- formData: any, loadExternalAssets: boolean, debug: boolean,
98
- } = {
99
- schema: null, layout: null, data: null, options: null, framework: null,
100
- widgets: null, form: null, model: null, JSONSchema: null, UISchema: null,
101
- formData: null, loadExternalAssets: null, debug: null,
102
- };
103
-
104
- // Recommended inputs
105
- @Input() schema: any; // The JSON Schema
106
- @Input() layout: any[]; // The form layout
107
- @Input() data: any; // The form data
108
- @Input() options: any; // The global form options
109
- @Input() framework: any | string; // The framework to load
110
- @Input() widgets: any; // Any custom widgets to load
111
-
112
- // Alternate combined single input
113
- @Input() form: any; // For testing, and JSON Schema Form API compatibility
114
-
115
- // Angular Schema Form API compatibility input
116
- @Input() model: any; // Alternate input for form data
117
-
118
- // React JSON Schema Form API compatibility inputs
119
- @Input() JSONSchema: any; // Alternate input for JSON Schema
120
- @Input() UISchema: any; // UI schema - alternate form layout format
121
- @Input() formData: any; // Alternate input for form data
122
-
123
- @Input() ngModel: any; // Alternate input for Angular forms
124
-
125
- @Input() language: string; // Language
126
-
127
- // Development inputs, for testing and debugging
128
- @Input() loadExternalAssets: boolean; // Load external framework assets?
129
- @Input() debug: boolean; // Show debug information?
130
-
131
- @Input() theme: string; // Theme
132
-
133
- @Input()
134
- get value(): any {
135
- return this.objectWrap ? this.jsf.data['1'] : this.jsf.data;
136
- }
137
- set value(value: any) {
138
- this.setFormValues(value, false);
139
- }
140
-
141
- // Outputs
142
- @Output() onChanges = new EventEmitter<any>(); // Live unvalidated internal form data
143
- @Output() onSubmit = new EventEmitter<any>(); // Complete validated form data
144
- @Output() isValid = new EventEmitter<boolean>(); // Is current data valid?
145
- @Output() validationErrors = new EventEmitter<any>(); // Validation errors (if any)
146
- @Output() formSchema = new EventEmitter<any>(); // Final schema used to create form
147
- @Output() formLayout = new EventEmitter<any>(); // Final layout used to create form
148
-
149
- // Outputs for possible 2-way data binding
150
- // Only the one input providing the initial form data will be bound.
151
- // If there is no inital data, input '{}' to activate 2-way data binding.
152
- // There is no 2-way binding if inital data is combined inside the 'form' input.
153
- @Output() dataChange = new EventEmitter<any>();
154
- @Output() modelChange = new EventEmitter<any>();
155
- @Output() formDataChange = new EventEmitter<any>();
156
- @Output() ngModelChange = new EventEmitter<any>();
157
-
158
- onChange: Function;
159
- onTouched: Function;
160
-
161
- //TODO-review,maybe use takeUntilDestroyed rxjs op
162
- dataChangesSubs:Subscription;
163
- statusChangesSubs:Subscription;
164
- isValidChangesSubs:Subscription;
165
- validationErrorChangesSubs:Subscription;
166
-
167
- constructor(
168
- private changeDetector: ChangeDetectorRef,
169
- private frameworkLibrary: FrameworkLibraryService,
170
- private widgetLibrary: WidgetLibraryService,
171
- public jsf: JsonSchemaFormService,
172
- ) { }
173
- ngOnDestroy(): void {
174
- this.dataChangesSubs?.unsubscribe();
175
- this.statusChangesSubs?.unsubscribe();
176
- this.isValidChangesSubs?.unsubscribe();
177
- this.validationErrorChangesSubs?.unsubscribe();
178
- this.dataChangesSubs=null;
179
- this.statusChangesSubs=null;
180
- this.isValidChangesSubs=null;
181
- this.validationErrorChangesSubs=null;
182
- }
183
-
184
- private resetScriptsAndStyleSheets() {
185
- document.querySelectorAll('.ajsf').forEach(element => element.remove());
186
- }
187
- private loadScripts(scriptList?:string[]) {
188
- const scripts = scriptList||this.frameworkLibrary.getFrameworkScripts();
189
- scripts.map(script => {
190
- const scriptTag: HTMLScriptElement = document.createElement('script');
191
- scriptTag.src = script;
192
- scriptTag.type = 'text/javascript';
193
- scriptTag.async = true;
194
- scriptTag.setAttribute('class', 'ajsf');
195
- document.getElementsByTagName('head')[0].appendChild(scriptTag);
196
- });
197
- }
198
- private loadStyleSheets(styleList?:string[]) {
199
- const stylesheets = styleList||this.frameworkLibrary.getFrameworkStylesheets();
200
- stylesheets.map(stylesheet => {
201
- const linkTag: HTMLLinkElement = document.createElement('link');
202
- linkTag.rel = 'stylesheet';
203
- linkTag.href = stylesheet;
204
- linkTag.setAttribute('class', 'ajsf');
205
- document.getElementsByTagName('head')[0].appendChild(linkTag);
206
- });
207
- }
208
- private loadAssets() {
209
- this.frameworkLibrary.getFrameworkAssetConfig().then(assetCfg=>{
210
- this.resetScriptsAndStyleSheets();
211
- this.loadScripts(assetCfg.scripts);
212
- this.loadStyleSheets(assetCfg.stylesheets);
213
- }).catch(err=>{
214
- console.log(err);
215
- this.resetScriptsAndStyleSheets();
216
- this.loadScripts();
217
- this.loadStyleSheets();
218
- })
219
-
220
- }
221
- ngOnInit() {
222
- this.updateForm();
223
- this.loadAssets();
224
- }
225
-
226
- ngOnChanges(changes: SimpleChanges) {
227
- this.updateForm();
228
- // Check if there's changes in Framework then load assets if that's the
229
- if (changes.framework) {
230
- if (!changes.framework.isFirstChange() &&
231
- (changes.framework.previousValue !== changes.framework.currentValue)) {
232
- this.loadAssets();
233
- }
234
- }
235
- }
236
-
237
- writeValue(value: any) {
238
- this.setFormValues(value, false);
239
- if (!this.formValuesInput) { this.formValuesInput = 'ngModel'; }
240
- }
241
-
242
- registerOnChange(fn: Function) {
243
- this.onChange = fn;
244
- }
245
-
246
- registerOnTouched(fn: Function) {
247
- this.onTouched = fn;
248
- }
249
-
250
- //see note
251
- //https://angular.io/guide/update-to-version-15#v15-bc-06
252
- setDisabledState(isDisabled: boolean) {
253
- if (this.jsf.formOptions.formDisabled !== !!isDisabled) {
254
- this.jsf.formOptions.formDisabled = !!isDisabled;
255
- this.initializeForm();
256
- }
257
- }
258
-
259
- updateForm() {
260
- let changedData;
261
- if (!this.formInitialized || !this.formValuesInput ||
262
- (this.language && this.language !== this.jsf.language)
263
-
264
- ) {
265
- this.initializeForm();
266
- } else {
267
- if (this.language && this.language !== this.jsf.language) {
268
- this.jsf.setLanguage(this.language);
269
- }
270
-
271
- // Get names of changed inputs
272
- let changedInput = Object.keys(this.previousInputs)
273
- .filter(input => this.previousInputs[input] !== this[input]);
274
- let resetFirst = true;
275
- if (changedInput.length === 1 && changedInput[0] === 'form' &&
276
- this.formValuesInput.startsWith('form.')
277
- ) {
278
- // If only 'form' input changed, get names of changed keys
279
- changedInput = Object.keys(this.previousInputs.form || {})
280
- .filter(key => !isEqual(this.previousInputs.form[key], this.form[key]))
281
- .map(key => `form.${key}`);
282
- resetFirst = false;
283
- }
284
-
285
- // If only input values have changed, update the form values
286
- if (changedInput.length === 1 && changedInput[0] === this.formValuesInput) {
287
- if (this.formValuesInput.indexOf('.') === -1) {
288
- changedData=this[this.formValuesInput];
289
- this.setFormValues(changedData, resetFirst);
290
- } else {
291
- const [input, key] = this.formValuesInput.split('.');
292
- changedData=this[input][key];
293
- this.setFormValues(changedData, resetFirst);
294
- }
295
-
296
- // If anything else has changed, re-render the entire form
297
- } else if (changedInput.length) {
298
- this.initializeForm(changedData);
299
- if (this.onChange) { this.onChange(this.jsf.formValues); }
300
- if (this.onTouched) { this.onTouched(this.jsf.formValues); }
301
- }
302
-
303
- //set framework theme
304
- if (this.theme && this.theme !== this.frameworkLibrary.getActiveTheme()?.name) {
305
- this.frameworkLibrary.requestThemeChange(this.theme);
306
- }
307
-
308
- // Update previous inputs
309
- Object.keys(this.previousInputs)
310
- .filter(input => this.previousInputs[input] !== this[input])
311
- .forEach(input => this.previousInputs[input] = this[input]);
312
- }
313
- }
314
-
315
- setFormValues(formValues: any, resetFirst = true) {
316
- if (formValues) {
317
- const newFormValues = this.objectWrap ? formValues['1'] : formValues;
318
- if (!this.jsf.formGroup) {
319
- this.jsf.formValues = formValues;
320
- this.activateForm();
321
- } else if (resetFirst) {//changed to avoid reset events
322
- this.jsf.formGroup.reset({},{emitEvent:false});
323
- }
324
- if (this.jsf.formGroup) {//changed to avoid reset events
325
- this.jsf.formGroup.patchValue(newFormValues,{emitEvent:false});
326
- }
327
- if (this.onChange) { this.onChange(newFormValues); }
328
- if (this.onTouched) { this.onTouched(newFormValues); }
329
- } else {
330
- this.jsf.formGroup.reset();
331
- }
332
- }
333
-
334
- submitForm() {
335
- const validData = this.jsf.validData;
336
- this.onSubmit.emit(this.objectWrap ? validData['1'] : validData);
337
- }
338
-
339
- /**
340
- * 'initializeForm' function
341
- *
342
- * - Update 'schema', 'layout', and 'formValues', from inputs.
343
- *
344
- * - Create 'schemaRefLibrary' and 'schemaRecursiveRefMap'
345
- * to resolve schema $ref links, including recursive $ref links.
346
- *
347
- * - Create 'dataRecursiveRefMap' to resolve recursive links in data
348
- * and corectly set output formats for recursively nested values.
349
- *
350
- * - Create 'layoutRefLibrary' and 'templateRefLibrary' to store
351
- * new layout nodes and formGroup elements to use when dynamically
352
- * adding form components to arrays and recursive $ref points.
353
- *
354
- * - Create 'dataMap' to map the data to the schema and template.
355
- *
356
- * - Create the master 'formGroupTemplate' then from it 'formGroup'
357
- * the Angular formGroup used to control the reactive form.
358
- */
359
- initializeForm(initialData?:any) {
360
- if (
361
- this.schema || this.layout || this.data || this.form || this.model ||
362
- this.JSONSchema || this.UISchema || this.formData || this.ngModel ||
363
- this.jsf.data
364
- ) {
365
- // Reset all form values to defaults
366
- this.jsf.resetAllValues();
367
- this.initializeOptions(); // Update options
368
- this.initializeSchema(); // Update schema, schemaRefLibrary,
369
- // schemaRecursiveRefMap, & dataRecursiveRefMap
370
- this.initializeLayout(); // Update layout, layoutRefLibrary,
371
- this.initializeData(); // Update formValues
372
- if(initialData){
373
- this.jsf.formValues=initialData;
374
- }
375
- this.activateForm(); // Update dataMap, templateRefLibrary,
376
- // formGroupTemplate, formGroup
377
-
378
- // Uncomment individual lines to output debugging information to console:
379
- // (These always work.)
380
- // console.log('loading form...');
381
- // console.log('schema', this.jsf.schema);
382
- // console.log('layout', this.jsf.layout);
383
- // console.log('options', this.options);
384
- // console.log('formValues', this.jsf.formValues);
385
- // console.log('formGroupTemplate', this.jsf.formGroupTemplate);
386
- // console.log('formGroup', this.jsf.formGroup);
387
- // console.log('formGroup.value', this.jsf.formGroup.value);
388
- // console.log('schemaRefLibrary', this.jsf.schemaRefLibrary);
389
- // console.log('layoutRefLibrary', this.jsf.layoutRefLibrary);
390
- // console.log('templateRefLibrary', this.jsf.templateRefLibrary);
391
- // console.log('dataMap', this.jsf.dataMap);
392
- // console.log('arrayMap', this.jsf.arrayMap);
393
- // console.log('schemaRecursiveRefMap', this.jsf.schemaRecursiveRefMap);
394
- // console.log('dataRecursiveRefMap', this.jsf.dataRecursiveRefMap);
395
-
396
- // Uncomment individual lines to output debugging information to browser:
397
- // (These only work if the 'debug' option has also been set to 'true'.)
398
- if (this.debug || this.jsf.formOptions.debug) {
399
- const vars: any[] = [];
400
- // vars.push(this.jsf.schema);
401
- // vars.push(this.jsf.layout);
402
- // vars.push(this.options);
403
- // vars.push(this.jsf.formValues);
404
- // vars.push(this.jsf.formGroup.value);
405
- // vars.push(this.jsf.formGroupTemplate);
406
- // vars.push(this.jsf.formGroup);
407
- // vars.push(this.jsf.schemaRefLibrary);
408
- // vars.push(this.jsf.layoutRefLibrary);
409
- // vars.push(this.jsf.templateRefLibrary);
410
- // vars.push(this.jsf.dataMap);
411
- // vars.push(this.jsf.arrayMap);
412
- // vars.push(this.jsf.schemaRecursiveRefMap);
413
- // vars.push(this.jsf.dataRecursiveRefMap);
414
- this.debugOutput = vars.map(v => JSON.stringify(v, null, 2)).join('\n');
415
- }
416
- this.formInitialized = true;
417
- }
418
- }
419
-
420
- /**
421
- * 'initializeOptions' function
422
- *
423
- * Initialize 'options' (global form options) and set framework
424
- * Combine available inputs:
425
- * 1. options - recommended
426
- * 2. form.options - Single input style
427
- */
428
- private initializeOptions() {
429
- if (this.language && this.language !== this.jsf.language) {
430
- this.jsf.setLanguage(this.language);
431
- }
432
- this.jsf.setOptions({ debug: !!this.debug });
433
- let loadExternalAssets: boolean = this.loadExternalAssets || false;
434
- let framework: any = this.framework || 'default';
435
- if (isObject(this.options)) {
436
- this.jsf.setOptions(this.options);
437
- loadExternalAssets = this.options.loadExternalAssets || loadExternalAssets;
438
- framework = this.options.framework || framework;
439
- }
440
- if (isObject(this.form) && isObject(this.form.options)) {
441
- this.jsf.setOptions(this.form.options);
442
- loadExternalAssets = this.form.options.loadExternalAssets || loadExternalAssets;
443
- framework = this.form.options.framework || framework;
444
- }
445
- if (isObject(this.widgets)) {
446
- this.jsf.setOptions({ widgets: this.widgets });
447
- }
448
- this.frameworkLibrary.setLoadExternalAssets(loadExternalAssets);
449
- this.frameworkLibrary.setFramework(framework);
450
- this.jsf.framework = this.frameworkLibrary.getFramework();
451
- if (isObject(this.jsf.formOptions.widgets)) {
452
- for (const widget of Object.keys(this.jsf.formOptions.widgets)) {
453
- this.widgetLibrary.registerWidget(widget, this.jsf.formOptions.widgets[widget]);
454
- }
455
- }
456
- if (isObject(this.form) && isObject(this.form.tpldata)) {
457
- this.jsf.setTpldata(this.form.tpldata);
458
- }
459
- if (this.theme) {
460
- this.frameworkLibrary.requestThemeChange(this.theme);
461
- }
462
- }
463
-
464
-
465
- /**
466
- * 'initializeSchema' function
467
- *
468
- * Initialize 'schema'
469
- * Use first available input:
470
- * 1. schema - recommended / Angular Schema Form style
471
- * 2. form.schema - Single input / JSON Form style
472
- * 3. JSONSchema - React JSON Schema Form style
473
- * 4. form.JSONSchema - For testing single input React JSON Schema Forms
474
- * 5. form - For testing single schema-only inputs
475
- *
476
- * ... if no schema input found, the 'activateForm' function, below,
477
- * will make two additional attempts to build a schema
478
- * 6. If layout input - build schema from layout
479
- * 7. If data input - build schema from data
480
- */
481
- private initializeSchema() {
482
-
483
- // TODO: update to allow non-object schemas
484
-
485
- if (isObject(this.schema)) {
486
- this.jsf.AngularSchemaFormCompatibility = true;
487
- this.jsf.schema = cloneDeep(this.schema);
488
- } else if (hasOwn(this.form, 'schema') && isObject(this.form.schema)) {
489
- this.jsf.schema = cloneDeep(this.form.schema);
490
- } else if (isObject(this.JSONSchema)) {
491
- this.jsf.ReactJsonSchemaFormCompatibility = true;
492
- this.jsf.schema = cloneDeep(this.JSONSchema);
493
- } else if (hasOwn(this.form, 'JSONSchema') && isObject(this.form.JSONSchema)) {
494
- this.jsf.ReactJsonSchemaFormCompatibility = true;
495
- this.jsf.schema = cloneDeep(this.form.JSONSchema);
496
- } else if (hasOwn(this.form, 'properties') && isObject(this.form.properties)) {
497
- this.jsf.schema = cloneDeep(this.form);
498
- } else if (isObject(this.form)) {
499
- // TODO: Handle other types of form input
500
- }
501
-
502
- if (!isEmpty(this.jsf.schema)) {
503
-
504
- // If other types also allowed, render schema as an object
505
- if (inArray('object', this.jsf.schema.type)) {
506
- this.jsf.schema.type = 'object';
507
- }
508
-
509
- // Wrap non-object schemas in object.
510
- if (hasOwn(this.jsf.schema, 'type') && this.jsf.schema.type !== 'object') {
511
- this.jsf.schema = {
512
- 'type': 'object',
513
- 'properties': { 1: this.jsf.schema }
514
- };
515
- this.objectWrap = true;
516
- } else if (!hasOwn(this.jsf.schema, 'type')) {
517
-
518
- // Add type = 'object' if missing
519
- if (isObject(this.jsf.schema.properties) ||
520
- isObject(this.jsf.schema.patternProperties) ||
521
- isObject(this.jsf.schema.additionalProperties)
522
- ) {
523
- this.jsf.schema.type = 'object';
524
-
525
- // Fix JSON schema shorthand (JSON Form style)
526
- } else {
527
- this.jsf.JsonFormCompatibility = true;
528
- this.jsf.schema = {
529
- 'type': 'object',
530
- 'properties': this.jsf.schema
531
- };
532
- }
533
- }
534
-
535
- // If needed, update JSON Schema to draft 6 format, including
536
- // draft 3 (JSON Form style) and draft 4 (Angular Schema Form style)
537
- this.jsf.schema = convertSchemaToDraft6(this.jsf.schema);
538
-
539
- // Initialize ajv and compile schema
540
- this.jsf.compileAjvSchema();
541
-
542
- // Create schemaRefLibrary, schemaRecursiveRefMap, dataRecursiveRefMap, & arrayMap
543
- this.jsf.schema = resolveSchemaReferences(
544
- this.jsf.schema, this.jsf.schemaRefLibrary, this.jsf.schemaRecursiveRefMap,
545
- this.jsf.dataRecursiveRefMap, this.jsf.arrayMap
546
- );
547
- if (hasOwn(this.jsf.schemaRefLibrary, '')) {
548
- this.jsf.hasRootReference = true;
549
- }
550
-
551
- // TODO: (?) Resolve external $ref links
552
- // // Create schemaRefLibrary & schemaRecursiveRefMap
553
- // this.parser.bundle(this.schema)
554
- // .then(schema => this.schema = resolveSchemaReferences(
555
- // schema, this.jsf.schemaRefLibrary,
556
- // this.jsf.schemaRecursiveRefMap, this.jsf.dataRecursiveRefMap
557
- // ));
558
- }
559
- }
560
-
561
- /**
562
- * 'initializeData' function
563
- *
564
- * Initialize 'formValues'
565
- * defulat or previously submitted values used to populate form
566
- * Use first available input:
567
- * 1. data - recommended
568
- * 2. model - Angular Schema Form style
569
- * 3. form.value - JSON Form style
570
- * 4. form.data - Single input style
571
- * 5. formData - React JSON Schema Form style
572
- * 6. form.formData - For easier testing of React JSON Schema Forms
573
- * 7. (none) no data - initialize data from schema and layout defaults only
574
- */
575
- private initializeData() {
576
- if (hasValue(this.data)) {
577
- this.jsf.formValues = cloneDeep(this.data);
578
- this.formValuesInput = 'data';
579
- } else if (hasValue(this.model)) {
580
- this.jsf.AngularSchemaFormCompatibility = true;
581
- this.jsf.formValues = cloneDeep(this.model);
582
- this.formValuesInput = 'model';
583
- } else if (hasValue(this.ngModel)) {
584
- this.jsf.AngularSchemaFormCompatibility = true;
585
- this.jsf.formValues = cloneDeep(this.ngModel);
586
- this.formValuesInput = 'ngModel';
587
- } else if (isObject(this.form) && hasValue(this.form.value)) {
588
- this.jsf.JsonFormCompatibility = true;
589
- this.jsf.formValues = cloneDeep(this.form.value);
590
- this.formValuesInput = 'form.value';
591
- } else if (isObject(this.form) && hasValue(this.form.data)) {
592
- this.jsf.formValues = cloneDeep(this.form.data);
593
- this.formValuesInput = 'form.data';
594
- } else if (hasValue(this.formData)) {
595
- this.jsf.ReactJsonSchemaFormCompatibility = true;
596
- this.formValuesInput = 'formData';
597
- } else if (hasOwn(this.form, 'formData') && hasValue(this.form.formData)) {
598
- this.jsf.ReactJsonSchemaFormCompatibility = true;
599
- this.jsf.formValues = cloneDeep(this.form.formData);
600
- this.formValuesInput = 'form.formData';
601
- } else {
602
- this.formValuesInput = "data";//null;
603
- }
604
- }
605
-
606
- /**
607
- * 'initializeLayout' function
608
- *
609
- * Initialize 'layout'
610
- * Use first available array input:
611
- * 1. layout - recommended
612
- * 2. form - Angular Schema Form style
613
- * 3. form.form - JSON Form style
614
- * 4. form.layout - Single input style
615
- * 5. (none) no layout - set default layout instead
616
- * (full layout will be built later from the schema)
617
- *
618
- * Also, if alternate layout formats are available,
619
- * import from 'UISchema' or 'customFormItems'
620
- * used for React JSON Schema Form and JSON Form API compatibility
621
- * Use first available input:
622
- * 1. UISchema - React JSON Schema Form style
623
- * 2. form.UISchema - For testing single input React JSON Schema Forms
624
- * 2. form.customFormItems - JSON Form style
625
- * 3. (none) no input - don't import
626
- */
627
- private initializeLayout() {
628
-
629
- // Rename JSON Form-style 'options' lists to
630
- // Angular Schema Form-style 'titleMap' lists.
631
- const fixJsonFormOptions = (layout: any): any => {
632
- if (isObject(layout) || isArray(layout)) {
633
- forEach(layout, (value, key) => {
634
- if (hasOwn(value, 'options') && isObject(value.options)) {
635
- value.titleMap = value.options;
636
- delete value.options;
637
- }
638
- }, 'top-down');
639
- }
640
- return layout;
641
- };
642
-
643
- // Check for layout inputs and, if found, initialize form layout
644
- if (isArray(this.layout)) {
645
- this.jsf.layout = cloneDeep(this.layout);
646
- } else if (isArray(this.form)) {
647
- this.jsf.AngularSchemaFormCompatibility = true;
648
- this.jsf.layout = cloneDeep(this.form);
649
- } else if (this.form && isArray(this.form.form)) {
650
- this.jsf.JsonFormCompatibility = true;
651
- this.jsf.layout = fixJsonFormOptions(cloneDeep(this.form.form));
652
- } else if (this.form && isArray(this.form.layout)) {
653
- this.jsf.layout = cloneDeep(this.form.layout);
654
- } else {
655
- this.jsf.layout = ['*'];
656
- }
657
-
658
- // Check for alternate layout inputs
659
- let alternateLayout: any = null;
660
- if (isObject(this.UISchema)) {
661
- this.jsf.ReactJsonSchemaFormCompatibility = true;
662
- alternateLayout = cloneDeep(this.UISchema);
663
- } else if (hasOwn(this.form, 'UISchema')) {
664
- this.jsf.ReactJsonSchemaFormCompatibility = true;
665
- alternateLayout = cloneDeep(this.form.UISchema);
666
- } else if (hasOwn(this.form, 'uiSchema')) {
667
- this.jsf.ReactJsonSchemaFormCompatibility = true;
668
- alternateLayout = cloneDeep(this.form.uiSchema);
669
- } else if (hasOwn(this.form, 'customFormItems')) {
670
- this.jsf.JsonFormCompatibility = true;
671
- alternateLayout = fixJsonFormOptions(cloneDeep(this.form.customFormItems));
672
- }
673
-
674
- // if alternate layout found, copy alternate layout options into schema
675
- if (alternateLayout) {
676
- JsonPointer.forEachDeep(alternateLayout, (value, pointer) => {
677
- const schemaPointer = pointer
678
- .replace(/\//g, '/properties/')
679
- .replace(/\/properties\/items\/properties\//g, '/items/properties/')
680
- .replace(/\/properties\/titleMap\/properties\//g, '/titleMap/properties/');
681
- if (hasValue(value) && hasValue(pointer)) {
682
- let key = JsonPointer.toKey(pointer);
683
- const groupPointer = (JsonPointer.parse(schemaPointer) || []).slice(0, -2);
684
- let itemPointer: string | string[];
685
-
686
- // If 'ui:order' object found, copy into object schema root
687
- if (key.toLowerCase() === 'ui:order') {
688
- itemPointer = [...groupPointer, 'ui:order'];
689
-
690
- // Copy other alternate layout options to schema 'x-schema-form',
691
- // (like Angular Schema Form options) and remove any 'ui:' prefixes
692
- } else {
693
- if (key.slice(0, 3).toLowerCase() === 'ui:') { key = key.slice(3); }
694
- itemPointer = [...groupPointer, 'x-schema-form', key];
695
- }
696
- if (JsonPointer.has(this.jsf.schema, groupPointer) &&
697
- !JsonPointer.has(this.jsf.schema, itemPointer)
698
- ) {
699
- JsonPointer.set(this.jsf.schema, itemPointer, value);
700
- }
701
- }
702
- });
703
- }
704
- }
705
-
706
- /**
707
- * 'activateForm' function
708
- *
709
- * ...continued from 'initializeSchema' function, above
710
- * If 'schema' has not been initialized (i.e. no schema input found)
711
- * 6. If layout input - build schema from layout input
712
- * 7. If data input - build schema from data input
713
- *
714
- * Create final layout,
715
- * build the FormGroup template and the Angular FormGroup,
716
- * subscribe to changes,
717
- * and activate the form.
718
- */
719
- private activateForm() {
720
- this.unsubscribeOnActivateForm$.next();
721
- // If 'schema' not initialized
722
- if (isEmpty(this.jsf.schema)) {
723
-
724
- // TODO: If full layout input (with no '*'), build schema from layout
725
- // if (!this.jsf.layout.includes('*')) {
726
- // this.jsf.buildSchemaFromLayout();
727
- // } else
728
-
729
- // If data input, build schema from data
730
- if (!isEmpty(this.jsf.formValues)) {
731
- this.jsf.buildSchemaFromData();
732
- }
733
- }
734
-
735
- if (!isEmpty(this.jsf.schema)) {
736
-
737
- // If not already initialized, initialize ajv and compile schema
738
- this.jsf.compileAjvSchema();
739
-
740
- // Update all layout elements, add values, widgets, and validators,
741
- // replace any '*' with a layout built from all schema elements,
742
- // and update the FormGroup template with any new validators
743
- this.jsf.buildLayout(this.widgetLibrary);
744
-
745
- // Build the Angular FormGroup template from the schema
746
- this.jsf.buildFormGroupTemplate(this.jsf.formValues);
747
-
748
- // Build the real Angular FormGroup from the FormGroup template
749
- this.jsf.buildFormGroup();
750
- }
751
-
752
- if (this.jsf.formGroup) {
753
-
754
- // Reset initial form values
755
- if (!isEmpty(this.jsf.formValues) &&
756
- this.jsf.formOptions.setSchemaDefaults !== true &&
757
- this.jsf.formOptions.setLayoutDefaults !== true
758
- ) {
759
- this.setFormValues(this.jsf.formValues);
760
- }
761
-
762
- // TODO: Figure out how to display calculated values without changing object data
763
- // See http://ulion.github.io/jsonform/playground/?example=templating-values
764
- // Calculate references to other fields
765
- // if (!isEmpty(this.jsf.formGroup.value)) {
766
- // forEach(this.jsf.formGroup.value, (value, key, object, rootObject) => {
767
- // if (typeof value === 'string') {
768
- // object[key] = this.jsf.parseText(value, value, rootObject, key);
769
- // }
770
- // }, 'top-down');
771
- // }
772
-
773
- // Subscribe to form changes to output live data, validation, and errors
774
- this.dataChangesSubs=this.jsf.dataChanges.pipe(takeUntil(this.unsubscribeOnActivateForm$)).subscribe(data => {
775
- this.onChanges.emit(this.objectWrap ? data['1'] : data);
776
- if (this.formValuesInput && this.formValuesInput.indexOf('.') === -1) {
777
- this[`${this.formValuesInput}Change`].emit(this.objectWrap ? data['1'] : data);
778
- }
779
- });
780
-
781
- // Trigger change detection on statusChanges to show updated errors
782
- this.statusChangesSubs= this.jsf.formGroup.statusChanges.pipe(takeUntil(this.unsubscribeOnActivateForm$)).subscribe(() => this.changeDetector.markForCheck());
783
- this.isValidChangesSubs=this.jsf.isValidChanges.pipe(takeUntil(this.unsubscribeOnActivateForm$)).subscribe(isValid => this.isValid.emit(isValid));
784
- this.validationErrorChangesSubs=this.jsf.validationErrorChanges.pipe(takeUntil(this.unsubscribeOnActivateForm$)).subscribe(err => this.validationErrors.emit(err));
785
-
786
- // Output final schema, final layout, and initial data
787
- this.formSchema.emit(this.jsf.schema);
788
- this.formLayout.emit(this.jsf.layout);
789
- this.onChanges.emit(this.objectWrap ? this.jsf.data['1'] : this.jsf.data);
790
-
791
- // If validateOnRender, output initial validation and any errors
792
- const validateOnRender =
793
- JsonPointer.get(this.jsf, '/formOptions/validateOnRender');
794
- if (validateOnRender) { // validateOnRender === 'auto' || true
795
- const touchAll = (control) => {
796
- if (validateOnRender === true || hasValue(control.value)) {
797
- control.markAsTouched();
798
- }
799
- Object.keys(control.controls || {})
800
- .forEach(key => touchAll(control.controls[key]));
801
- };
802
- touchAll(this.jsf.formGroup);
803
- this.isValid.emit(this.jsf.isValid);
804
- this.validationErrors.emit(this.jsf.ajvErrors);
805
- }
806
- }
807
- }
808
-
809
- }