@ng-formworks/core 17.2.7 → 17.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +833 -0
- package/esm2022/lib/framework-library/framework-library.service.mjs +175 -0
- package/esm2022/lib/framework-library/framework.mjs +15 -0
- package/esm2022/lib/framework-library/no-framework.component.mjs +18 -0
- package/esm2022/lib/framework-library/no-framework.module.mjs +27 -0
- package/esm2022/lib/framework-library/no.framework.mjs +19 -0
- package/esm2022/lib/json-schema-form.component.mjs +765 -0
- package/esm2022/lib/json-schema-form.module.mjs +26 -0
- package/esm2022/lib/json-schema-form.service.mjs +676 -0
- package/esm2022/lib/locale/de-validation-messages.mjs +60 -0
- package/esm2022/lib/locale/en-validation-messages.mjs +60 -0
- package/esm2022/lib/locale/es-validation-messages.mjs +57 -0
- package/esm2022/lib/locale/fr-validation-messages.mjs +60 -0
- package/esm2022/lib/locale/index.mjs +8 -0
- package/esm2022/lib/locale/it-validation-messages.mjs +60 -0
- package/esm2022/lib/locale/pt-validation-messages.mjs +60 -0
- package/esm2022/lib/locale/zh-validation-messages.mjs +60 -0
- package/esm2022/lib/shared/convert-schema-to-draft6.function.mjs +300 -0
- package/esm2022/lib/shared/form-group.functions.mjs +442 -0
- package/esm2022/lib/shared/format-regex.constants.mjs +54 -0
- package/esm2022/lib/shared/index.mjs +12 -0
- package/esm2022/lib/shared/json-schema.functions.mjs +784 -0
- package/esm2022/lib/shared/json.validators.mjs +884 -0
- package/esm2022/lib/shared/jsonpointer.functions.mjs +1026 -0
- package/esm2022/lib/shared/layout.functions.mjs +1154 -0
- package/esm2022/lib/shared/merge-schemas.function.mjs +345 -0
- package/esm2022/lib/shared/utility.functions.mjs +380 -0
- package/esm2022/lib/shared/validator.functions.mjs +584 -0
- package/esm2022/lib/widget-library/add-reference.component.mjs +61 -0
- package/esm2022/lib/widget-library/button.component.mjs +72 -0
- package/esm2022/lib/widget-library/checkbox.component.mjs +105 -0
- package/esm2022/lib/widget-library/checkboxes.component.mjs +147 -0
- package/esm2022/lib/widget-library/file.component.mjs +35 -0
- package/esm2022/lib/widget-library/hidden.component.mjs +54 -0
- package/esm2022/lib/widget-library/index.mjs +55 -0
- package/esm2022/lib/widget-library/input.component.mjs +119 -0
- package/esm2022/lib/widget-library/message.component.mjs +38 -0
- package/esm2022/lib/widget-library/none.component.mjs +21 -0
- package/esm2022/lib/widget-library/number.component.mjs +123 -0
- package/esm2022/lib/widget-library/one-of.component.mjs +35 -0
- package/esm2022/lib/widget-library/orderable.directive.mjs +123 -0
- package/esm2022/lib/widget-library/radios.component.mjs +153 -0
- package/esm2022/lib/widget-library/root.component.mjs +79 -0
- package/esm2022/lib/widget-library/section.component.mjs +199 -0
- package/esm2022/lib/widget-library/select-framework.component.mjs +51 -0
- package/esm2022/lib/widget-library/select-widget.component.mjs +46 -0
- package/esm2022/lib/widget-library/select.component.mjs +150 -0
- package/esm2022/lib/widget-library/submit.component.mjs +82 -0
- package/esm2022/lib/widget-library/tab.component.mjs +41 -0
- package/esm2022/lib/widget-library/tabs.component.mjs +108 -0
- package/esm2022/lib/widget-library/template.component.mjs +46 -0
- package/esm2022/lib/widget-library/textarea.component.mjs +104 -0
- package/esm2022/lib/widget-library/widget-library.module.mjs +42 -0
- package/esm2022/lib/widget-library/widget-library.service.mjs +226 -0
- package/esm2022/ng-formworks-core.mjs +5 -0
- package/esm2022/public_api.mjs +13 -0
- package/fesm2022/ng-formworks-core.mjs +10147 -0
- package/fesm2022/ng-formworks-core.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/lib/framework-library/framework-library.service.d.ts +55 -0
- package/lib/framework-library/framework.d.ts +13 -0
- package/lib/framework-library/no-framework.component.d.ts +8 -0
- package/lib/framework-library/no-framework.module.d.ts +9 -0
- package/lib/framework-library/no.framework.d.ts +10 -0
- package/lib/json-schema-form.component.d.ts +218 -0
- package/lib/json-schema-form.module.d.ts +11 -0
- package/lib/json-schema-form.service.d.ts +115 -0
- package/lib/locale/de-validation-messages.d.ts +1 -0
- package/lib/locale/en-validation-messages.d.ts +1 -0
- package/lib/locale/es-validation-messages.d.ts +1 -0
- package/lib/locale/fr-validation-messages.d.ts +1 -0
- package/{src/lib/locale/index.ts → lib/locale/index.d.ts} +7 -7
- package/lib/locale/it-validation-messages.d.ts +1 -0
- package/lib/locale/pt-validation-messages.d.ts +1 -0
- package/lib/locale/zh-validation-messages.d.ts +1 -0
- package/lib/shared/convert-schema-to-draft6.function.d.ts +21 -0
- package/lib/shared/form-group.functions.d.ts +100 -0
- package/lib/shared/format-regex.constants.d.ts +19 -0
- package/lib/shared/index.d.ts +9 -0
- package/lib/shared/json-schema.functions.d.ts +193 -0
- package/lib/shared/json.validators.d.ts +441 -0
- package/lib/shared/jsonpointer.functions.d.ts +416 -0
- package/lib/shared/layout.functions.d.ts +83 -0
- package/lib/shared/merge-schemas.function.d.ts +19 -0
- package/lib/shared/utility.functions.d.ts +165 -0
- package/{src/lib/shared/validator.functions.ts → lib/shared/validator.functions.d.ts} +364 -601
- package/lib/widget-library/add-reference.component.d.ts +20 -0
- package/lib/widget-library/button.component.d.ts +21 -0
- package/lib/widget-library/checkbox.component.d.ts +24 -0
- package/lib/widget-library/checkboxes.component.d.ts +24 -0
- package/lib/widget-library/file.component.d.ts +21 -0
- package/lib/widget-library/hidden.component.d.ts +19 -0
- package/{src/lib/widget-library/index.ts → lib/widget-library/index.d.ts} +47 -56
- package/lib/widget-library/input.component.d.ts +22 -0
- package/lib/widget-library/message.component.d.ts +15 -0
- package/lib/widget-library/none.component.d.ts +8 -0
- package/lib/widget-library/number.component.d.ts +25 -0
- package/lib/widget-library/one-of.component.d.ts +21 -0
- package/lib/widget-library/orderable.directive.d.ts +41 -0
- package/lib/widget-library/radios.component.d.ts +23 -0
- package/lib/widget-library/root.component.d.ts +17 -0
- package/lib/widget-library/section.component.d.ts +19 -0
- package/lib/widget-library/select-framework.component.d.ts +18 -0
- package/lib/widget-library/select-widget.component.d.ts +18 -0
- package/lib/widget-library/select.component.d.ts +24 -0
- package/lib/widget-library/submit.component.d.ts +24 -0
- package/lib/widget-library/tab.component.d.ts +14 -0
- package/lib/widget-library/tabs.component.d.ts +20 -0
- package/lib/widget-library/template.component.d.ts +18 -0
- package/lib/widget-library/textarea.component.d.ts +21 -0
- package/lib/widget-library/widget-library.module.d.ts +31 -0
- package/lib/widget-library/widget-library.service.d.ts +22 -0
- package/package.json +64 -53
- package/{src/public_api.ts → public_api.d.ts} +9 -21
- package/karma.conf.js +0 -46
- package/ng-package.json +0 -11
- package/src/lib/framework-library/framework-library.service.ts +0 -195
- package/src/lib/framework-library/framework.ts +0 -11
- package/src/lib/framework-library/no-framework.component.html +0 -2
- package/src/lib/framework-library/no-framework.component.ts +0 -11
- package/src/lib/framework-library/no-framework.module.ts +0 -18
- package/src/lib/framework-library/no.framework.ts +0 -11
- package/src/lib/json-schema-form.component.html +0 -7
- package/src/lib/json-schema-form.component.ts +0 -809
- package/src/lib/json-schema-form.module.ts +0 -17
- package/src/lib/json-schema-form.service.ts +0 -907
- package/src/lib/locale/de-validation-messages.ts +0 -58
- package/src/lib/locale/en-validation-messages.ts +0 -58
- package/src/lib/locale/es-validation-messages.ts +0 -55
- package/src/lib/locale/fr-validation-messages.ts +0 -58
- package/src/lib/locale/it-validation-messages.ts +0 -58
- package/src/lib/locale/pt-validation-messages.ts +0 -58
- package/src/lib/locale/zh-validation-messages.ts +0 -58
- package/src/lib/locale-dates/en-US.ts +0 -5
- package/src/lib/shared/convert-schema-to-draft6.function.ts +0 -321
- package/src/lib/shared/form-group.functions.ts +0 -522
- package/src/lib/shared/format-regex.constants.ts +0 -73
- package/src/lib/shared/index.ts +0 -40
- package/src/lib/shared/json-schema.functions.ts +0 -788
- package/src/lib/shared/json.validators.ts +0 -878
- package/src/lib/shared/jsonpointer.functions.ts +0 -1012
- package/src/lib/shared/jspointer.functions.json.spec.ts +0 -103
- package/src/lib/shared/layout.functions.ts +0 -1233
- package/src/lib/shared/merge-schemas.function.ts +0 -329
- package/src/lib/shared/utility.functions.ts +0 -373
- package/src/lib/shared/validator.functions.spec.ts +0 -55
- package/src/lib/widget-library/add-reference.component.ts +0 -59
- package/src/lib/widget-library/button.component.ts +0 -54
- package/src/lib/widget-library/checkbox.component.ts +0 -74
- package/src/lib/widget-library/checkboxes.component.ts +0 -104
- package/src/lib/widget-library/file.component.ts +0 -36
- package/src/lib/widget-library/hidden.component.ts +0 -39
- package/src/lib/widget-library/input.component.ts +0 -76
- package/src/lib/widget-library/message.component.ts +0 -29
- package/src/lib/widget-library/none.component.ts +0 -12
- package/src/lib/widget-library/number.component.ts +0 -79
- package/src/lib/widget-library/one-of.component.ts +0 -36
- package/src/lib/widget-library/orderable.directive.ts +0 -130
- package/src/lib/widget-library/radios.component.ts +0 -101
- package/src/lib/widget-library/root.component.ts +0 -78
- package/src/lib/widget-library/section.component.ts +0 -133
- package/src/lib/widget-library/select-framework.component.ts +0 -50
- package/src/lib/widget-library/select-widget.component.ts +0 -46
- package/src/lib/widget-library/select.component.ts +0 -96
- package/src/lib/widget-library/submit.component.ts +0 -68
- package/src/lib/widget-library/tab.component.ts +0 -29
- package/src/lib/widget-library/tabs.component.ts +0 -83
- package/src/lib/widget-library/template.component.ts +0 -52
- package/src/lib/widget-library/textarea.component.ts +0 -68
- package/src/lib/widget-library/widget-library.module.ts +0 -13
- package/src/lib/widget-library/widget-library.service.ts +0 -234
- package/src/test.ts +0 -18
- package/tsconfig.lib.json +0 -25
- package/tsconfig.lib.prod.json +0 -9
- package/tsconfig.spec.json +0 -17
- 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
|
-
}
|