@ngx-formly/core 10.0.1 → 11.1.0-rc.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/README.md +103 -173
- package/fesm2022/ngx-formly-core-json-schema.mjs +670 -842
- package/fesm2022/ngx-formly-core-json-schema.mjs.map +1 -1
- package/fesm2022/ngx-formly-core-preset.mjs +76 -57
- package/fesm2022/ngx-formly-core-preset.mjs.map +1 -1
- package/fesm2022/ngx-formly-core-select.mjs +192 -134
- package/fesm2022/ngx-formly-core-select.mjs.map +1 -1
- package/fesm2022/ngx-formly-core-testing.mjs +112 -87
- package/fesm2022/ngx-formly-core-testing.mjs.map +1 -1
- package/fesm2022/ngx-formly-core.mjs +3560 -3031
- package/fesm2022/ngx-formly-core.mjs.map +1 -1
- package/package.json +2 -2
- package/types/ngx-formly-core-json-schema.d.ts +39 -41
- package/types/ngx-formly-core-preset.d.ts +8 -12
- package/types/ngx-formly-core-select.d.ts +35 -40
- package/types/ngx-formly-core-testing.d.ts +27 -29
- package/types/ngx-formly-core.d.ts +764 -659
|
@@ -1,857 +1,685 @@
|
|
|
1
|
-
import * as i0 from
|
|
2
|
-
import { Injectable } from
|
|
3
|
-
import { UntypedFormArray, UntypedFormGroup } from
|
|
4
|
-
import {
|
|
5
|
-
import { tap } from
|
|
6
|
-
|
|
7
|
-
// https://stackoverflow.com/a/27865285
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
import { Injectable } from "@angular/core";
|
|
3
|
+
import { UntypedFormArray, UntypedFormGroup } from "@angular/forms";
|
|
4
|
+
import { ɵclone, ɵgetFieldValue, ɵhasKey, ɵreverseDeepMerge } from "@ngx-formly/core";
|
|
5
|
+
import { tap } from "rxjs/operators";
|
|
8
6
|
function decimalPlaces(a) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
return p;
|
|
7
|
+
if (!isFinite(a)) return 0;
|
|
8
|
+
let e = 1, p = 0;
|
|
9
|
+
while (Math.round(a * e) / e !== a) {
|
|
10
|
+
e *= 10;
|
|
11
|
+
p++;
|
|
12
|
+
}
|
|
13
|
+
return p;
|
|
18
14
|
}
|
|
19
15
|
function isEmpty(v) {
|
|
20
|
-
|
|
16
|
+
return v === "" || v == null;
|
|
21
17
|
}
|
|
22
18
|
function isObject(v) {
|
|
23
|
-
|
|
19
|
+
return v != null && typeof v === "object" && !Array.isArray(v);
|
|
24
20
|
}
|
|
25
21
|
function isInteger(value) {
|
|
26
|
-
|
|
22
|
+
return Number.isInteger ? Number.isInteger(value) : typeof value === "number" && Math.floor(value) === value;
|
|
27
23
|
}
|
|
28
24
|
function isConst(schema) {
|
|
29
|
-
|
|
25
|
+
return typeof schema === "object" && (schema.hasOwnProperty("const") || schema.enum && schema.enum.length === 1);
|
|
30
26
|
}
|
|
31
27
|
function toNumber(value) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
if (typeof value === 'number') {
|
|
39
|
-
return value;
|
|
40
|
-
}
|
|
41
|
-
const val = parseFloat(value);
|
|
42
|
-
return !isNaN(val) ? val : value;
|
|
28
|
+
if (value === "" || value === void 0) return;
|
|
29
|
+
if (value === null) return null;
|
|
30
|
+
if (typeof value === "number") return value;
|
|
31
|
+
const val = parseFloat(value);
|
|
32
|
+
return !isNaN(val) ? val : value;
|
|
43
33
|
}
|
|
44
34
|
function totalMatchedFields(field) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
(value !== undefined && ((field.fieldArray && Array.isArray(value)) || (!field.fieldArray && isObject(value))))) {
|
|
53
|
-
return 1;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return total;
|
|
57
|
-
}
|
|
58
|
-
class FormlyJsonschema {
|
|
59
|
-
toFieldConfig(schema, options) {
|
|
60
|
-
schema = _clone(schema);
|
|
61
|
-
return this._toFieldConfig(schema, { schema, ...(options || {}) });
|
|
62
|
-
}
|
|
63
|
-
_toFieldConfig(schema, { key, isOptional, ...options }) {
|
|
64
|
-
schema = this.resolveSchema(schema, options);
|
|
65
|
-
const types = this.guessSchemaType(schema);
|
|
66
|
-
let field = {
|
|
67
|
-
type: types[0],
|
|
68
|
-
defaultValue: schema.default,
|
|
69
|
-
props: {
|
|
70
|
-
label: schema.title,
|
|
71
|
-
readonly: schema.readOnly,
|
|
72
|
-
description: schema.description,
|
|
73
|
-
},
|
|
74
|
-
};
|
|
75
|
-
if (key != null) {
|
|
76
|
-
field.key = key;
|
|
77
|
-
}
|
|
78
|
-
if (!options.ignoreDefault && (schema.readOnly || options.readOnly)) {
|
|
79
|
-
field.props.disabled = true;
|
|
80
|
-
options = { ...options, readOnly: true };
|
|
81
|
-
}
|
|
82
|
-
if (options.resetOnHide) {
|
|
83
|
-
field.resetOnHide = true;
|
|
84
|
-
}
|
|
85
|
-
if (options.shareFormControl === false) {
|
|
86
|
-
field.shareFormControl = false;
|
|
87
|
-
}
|
|
88
|
-
if (field.defaultValue === undefined && types.length === 1 && isOptional === false) {
|
|
89
|
-
switch (types[0]) {
|
|
90
|
-
case 'null': {
|
|
91
|
-
field.defaultValue = null;
|
|
92
|
-
break;
|
|
93
|
-
}
|
|
94
|
-
case 'string': {
|
|
95
|
-
if (!options.ignoreImplicitStringDefaults) {
|
|
96
|
-
field.defaultValue = '';
|
|
97
|
-
}
|
|
98
|
-
break;
|
|
99
|
-
}
|
|
100
|
-
case 'object': {
|
|
101
|
-
field.defaultValue = {};
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
case 'array': {
|
|
105
|
-
field.defaultValue = schema.minItems > 0 ? Array.from(new Array(schema.minItems)) : [];
|
|
106
|
-
break;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
if (options.ignoreDefault) {
|
|
111
|
-
delete field.defaultValue;
|
|
112
|
-
}
|
|
113
|
-
this.addValidator(field, 'type', {
|
|
114
|
-
schemaType: types,
|
|
115
|
-
expression: ({ value }) => {
|
|
116
|
-
if (value === undefined) {
|
|
117
|
-
return true;
|
|
118
|
-
}
|
|
119
|
-
if (value === null && types.indexOf('null') !== -1) {
|
|
120
|
-
return true;
|
|
121
|
-
}
|
|
122
|
-
switch (types[0]) {
|
|
123
|
-
case 'null': {
|
|
124
|
-
return typeof value === null;
|
|
125
|
-
}
|
|
126
|
-
case 'string': {
|
|
127
|
-
return typeof value === 'string';
|
|
128
|
-
}
|
|
129
|
-
case 'integer': {
|
|
130
|
-
return isInteger(value);
|
|
131
|
-
}
|
|
132
|
-
case 'number': {
|
|
133
|
-
return typeof value === 'number';
|
|
134
|
-
}
|
|
135
|
-
case 'object': {
|
|
136
|
-
return isObject(value);
|
|
137
|
-
}
|
|
138
|
-
case 'array': {
|
|
139
|
-
return Array.isArray(value);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return true;
|
|
143
|
-
},
|
|
144
|
-
});
|
|
145
|
-
switch (field.type) {
|
|
146
|
-
case 'number':
|
|
147
|
-
case 'integer': {
|
|
148
|
-
field.parsers = [
|
|
149
|
-
(v, f) => {
|
|
150
|
-
v = v === '' && types.includes('null') ? null : toNumber(v);
|
|
151
|
-
if (v === null && f) {
|
|
152
|
-
const input = typeof document !== 'undefined' && f.id
|
|
153
|
-
? document.querySelector(`#${f.id}`)
|
|
154
|
-
: undefined;
|
|
155
|
-
if (input && input.validity && !input.validity.badInput && !types.includes('null')) {
|
|
156
|
-
v = undefined;
|
|
157
|
-
}
|
|
158
|
-
if (v !== f.formControl.value) {
|
|
159
|
-
f.formControl.setValue(v, { emitModelToViewChange: false });
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
return v;
|
|
163
|
-
},
|
|
164
|
-
];
|
|
165
|
-
if (schema.hasOwnProperty('minimum')) {
|
|
166
|
-
field.props.min = schema.minimum;
|
|
167
|
-
}
|
|
168
|
-
if (schema.hasOwnProperty('maximum')) {
|
|
169
|
-
field.props.max = schema.maximum;
|
|
170
|
-
}
|
|
171
|
-
if (schema.hasOwnProperty('exclusiveMinimum')) {
|
|
172
|
-
field.props.exclusiveMinimum = schema.exclusiveMinimum;
|
|
173
|
-
this.addValidator(field, 'exclusiveMinimum', ({ value }) => isEmpty(value) || value > schema.exclusiveMinimum);
|
|
174
|
-
}
|
|
175
|
-
if (schema.hasOwnProperty('exclusiveMaximum')) {
|
|
176
|
-
field.props.exclusiveMaximum = schema.exclusiveMaximum;
|
|
177
|
-
this.addValidator(field, 'exclusiveMaximum', ({ value }) => isEmpty(value) || value < schema.exclusiveMaximum);
|
|
178
|
-
}
|
|
179
|
-
if (schema.hasOwnProperty('multipleOf')) {
|
|
180
|
-
field.props.step = schema.multipleOf;
|
|
181
|
-
this.addValidator(field, 'multipleOf', ({ value }) => {
|
|
182
|
-
if (isEmpty(value) || typeof value !== 'number' || value === 0 || schema.multipleOf <= 0) {
|
|
183
|
-
return true;
|
|
184
|
-
}
|
|
185
|
-
// https://github.com/ajv-validator/ajv/issues/652#issue-283610859
|
|
186
|
-
const multiplier = Math.pow(10, decimalPlaces(schema.multipleOf));
|
|
187
|
-
return Math.round(value * multiplier) % Math.round(schema.multipleOf * multiplier) === 0;
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
break;
|
|
191
|
-
}
|
|
192
|
-
case 'string': {
|
|
193
|
-
field.parsers = [
|
|
194
|
-
(v, f) => {
|
|
195
|
-
if (types.indexOf('null') !== -1) {
|
|
196
|
-
v = isEmpty(v) ? null : v;
|
|
197
|
-
}
|
|
198
|
-
else if (f && !f.props.required) {
|
|
199
|
-
v = v === '' ? undefined : v;
|
|
200
|
-
}
|
|
201
|
-
return v;
|
|
202
|
-
},
|
|
203
|
-
];
|
|
204
|
-
['minLength', 'maxLength', 'pattern'].forEach((prop) => {
|
|
205
|
-
if (schema.hasOwnProperty(prop)) {
|
|
206
|
-
field.props[prop] = schema[prop];
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
break;
|
|
210
|
-
}
|
|
211
|
-
case 'object': {
|
|
212
|
-
if (!field.fieldGroup) {
|
|
213
|
-
field.fieldGroup = [];
|
|
214
|
-
}
|
|
215
|
-
const { propDeps, schemaDeps } = this.resolveDependencies(schema);
|
|
216
|
-
Object.keys(schema.properties || {}).forEach((property) => {
|
|
217
|
-
const isRequired = Array.isArray(schema.required) && schema.required.indexOf(property) !== -1;
|
|
218
|
-
const f = this._toFieldConfig(schema.properties[property], {
|
|
219
|
-
...options,
|
|
220
|
-
key: property,
|
|
221
|
-
isOptional: isOptional || !isRequired,
|
|
222
|
-
});
|
|
223
|
-
field.fieldGroup.push(f);
|
|
224
|
-
if (isRequired || propDeps[property]) {
|
|
225
|
-
f.expressions = {
|
|
226
|
-
...(f.expressions || {}),
|
|
227
|
-
'props.required': (f) => {
|
|
228
|
-
let parent = f.parent;
|
|
229
|
-
const model = f.fieldGroup && f.key != null ? parent.model : f.model;
|
|
230
|
-
while (parent.key == null && parent.parent) {
|
|
231
|
-
parent = parent.parent;
|
|
232
|
-
}
|
|
233
|
-
const required = parent && parent.props ? parent.props.required : false;
|
|
234
|
-
if (!model && !required) {
|
|
235
|
-
return false;
|
|
236
|
-
}
|
|
237
|
-
if (Array.isArray(schema.required) && schema.required.indexOf(property) !== -1) {
|
|
238
|
-
return true;
|
|
239
|
-
}
|
|
240
|
-
return propDeps[property] && f.model && propDeps[property].some((k) => !isEmpty(f.model[k]));
|
|
241
|
-
},
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
if (schemaDeps[property]) {
|
|
245
|
-
const getConstValue = (s) => {
|
|
246
|
-
return s.hasOwnProperty('const') ? s.const : s.enum[0];
|
|
247
|
-
};
|
|
248
|
-
const oneOfSchema = schemaDeps[property].oneOf;
|
|
249
|
-
if (oneOfSchema &&
|
|
250
|
-
oneOfSchema.every((o) => o.properties && o.properties[property] && isConst(o.properties[property]))) {
|
|
251
|
-
oneOfSchema.forEach((oneOfSchemaItem) => {
|
|
252
|
-
const { [property]: constSchema, ...properties } = oneOfSchemaItem.properties;
|
|
253
|
-
field.fieldGroup.push({
|
|
254
|
-
...this._toFieldConfig({ ...oneOfSchemaItem, properties }, { ...options, shareFormControl: false, resetOnHide: true }),
|
|
255
|
-
expressions: {
|
|
256
|
-
hide: (f) => !f.model || getConstValue(constSchema) !== f.model[property],
|
|
257
|
-
},
|
|
258
|
-
});
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
else {
|
|
262
|
-
field.fieldGroup.push({
|
|
263
|
-
...this._toFieldConfig(schemaDeps[property], options),
|
|
264
|
-
expressions: {
|
|
265
|
-
hide: (f) => !f.model || isEmpty(f.model[property]),
|
|
266
|
-
},
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
});
|
|
271
|
-
if (schema.oneOf) {
|
|
272
|
-
field.fieldGroup.push(this.resolveMultiSchema('oneOf', schema.oneOf, { ...options, shareFormControl: false }));
|
|
273
|
-
}
|
|
274
|
-
if (schema.anyOf) {
|
|
275
|
-
field.fieldGroup.push(this.resolveMultiSchema('anyOf', schema.anyOf, options));
|
|
276
|
-
}
|
|
277
|
-
// Process if/then/else conditional schemas
|
|
278
|
-
if (options.conditionalSchemas) {
|
|
279
|
-
const conditionalSchemas = options.conditionalSchemas;
|
|
280
|
-
conditionalSchemas.forEach((conditionalSchema) => {
|
|
281
|
-
const condition = conditionalSchema._ifCondition;
|
|
282
|
-
if (condition && conditionalSchema.properties) {
|
|
283
|
-
// Create a field group with the conditional fields
|
|
284
|
-
const conditionalFieldGroup = {
|
|
285
|
-
fieldGroup: [],
|
|
286
|
-
expressions: {
|
|
287
|
-
hide: (f) => {
|
|
288
|
-
if (!f.model) {
|
|
289
|
-
return true;
|
|
290
|
-
}
|
|
291
|
-
const modelValue = f.model[condition.property];
|
|
292
|
-
const matches = modelValue === condition.value;
|
|
293
|
-
// If negate is true (for "else" case), invert the condition
|
|
294
|
-
return condition.negate ? matches : !matches;
|
|
295
|
-
},
|
|
296
|
-
},
|
|
297
|
-
};
|
|
298
|
-
// Add each property from the conditional schema to the field group
|
|
299
|
-
Object.keys(conditionalSchema.properties).forEach((property) => {
|
|
300
|
-
const propSchema = conditionalSchema.properties[property];
|
|
301
|
-
if (!propSchema) {
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
const isRequired = Array.isArray(conditionalSchema.required) && conditionalSchema.required.indexOf(property) !== -1;
|
|
305
|
-
const conditionalField = this._toFieldConfig(propSchema, {
|
|
306
|
-
...options,
|
|
307
|
-
key: property,
|
|
308
|
-
isOptional: !isRequired,
|
|
309
|
-
resetOnHide: true,
|
|
310
|
-
});
|
|
311
|
-
conditionalFieldGroup.fieldGroup.push(conditionalField);
|
|
312
|
-
});
|
|
313
|
-
field.fieldGroup.push(conditionalFieldGroup);
|
|
314
|
-
}
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
break;
|
|
318
|
-
}
|
|
319
|
-
case 'array': {
|
|
320
|
-
if (schema.hasOwnProperty('minItems')) {
|
|
321
|
-
field.props.minItems = schema.minItems;
|
|
322
|
-
this.addValidator(field, 'minItems', ({ value }) => {
|
|
323
|
-
return isEmpty(value) || value.length >= schema.minItems;
|
|
324
|
-
});
|
|
325
|
-
if (!isOptional && schema.minItems > 0 && field.defaultValue === undefined) {
|
|
326
|
-
field.defaultValue = Array.from(new Array(schema.minItems));
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
if (schema.hasOwnProperty('maxItems')) {
|
|
330
|
-
field.props.maxItems = schema.maxItems;
|
|
331
|
-
this.addValidator(field, 'maxItems', ({ value }) => {
|
|
332
|
-
return isEmpty(value) || value.length <= schema.maxItems;
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
if (schema.hasOwnProperty('uniqueItems')) {
|
|
336
|
-
field.props.uniqueItems = schema.uniqueItems;
|
|
337
|
-
this.addValidator(field, 'uniqueItems', ({ value }) => {
|
|
338
|
-
if (isEmpty(value) || !schema.uniqueItems) {
|
|
339
|
-
return true;
|
|
340
|
-
}
|
|
341
|
-
const uniqueItems = Array.from(new Set(value.map((v) => JSON.stringify(v, (k, o) => {
|
|
342
|
-
if (isObject(o)) {
|
|
343
|
-
return Object.keys(o)
|
|
344
|
-
.sort()
|
|
345
|
-
.reduce((obj, key) => {
|
|
346
|
-
obj[key] = o[key];
|
|
347
|
-
return obj;
|
|
348
|
-
}, {});
|
|
349
|
-
}
|
|
350
|
-
return o;
|
|
351
|
-
}))));
|
|
352
|
-
return uniqueItems.length === value.length;
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
// resolve items schema needed for isEnum check
|
|
356
|
-
if (schema.items && !Array.isArray(schema.items)) {
|
|
357
|
-
schema.items = this.resolveSchema(schema.items, options);
|
|
358
|
-
}
|
|
359
|
-
// TODO: remove isEnum check once adding an option to skip extension
|
|
360
|
-
if (!this.isEnum(schema)) {
|
|
361
|
-
field.fieldArray = (root) => {
|
|
362
|
-
const length = root.fieldGroup ? root.fieldGroup.length : 0;
|
|
363
|
-
const items = schema.items;
|
|
364
|
-
if (!Array.isArray(items)) {
|
|
365
|
-
if (!items) {
|
|
366
|
-
return {};
|
|
367
|
-
}
|
|
368
|
-
const isMultiSchema = items.oneOf || items.anyOf;
|
|
369
|
-
// When items is a single schema, the additionalItems keyword is meaningless, and it should not be used.
|
|
370
|
-
const f = this._toFieldConfig(items, isMultiSchema ? { ...options, key: `${length}`, isOptional: false } : { ...options, isOptional: false });
|
|
371
|
-
if (isMultiSchema && !_hasKey(f)) {
|
|
372
|
-
f.key = null;
|
|
373
|
-
}
|
|
374
|
-
return f;
|
|
375
|
-
}
|
|
376
|
-
const itemSchema = items[length] ? items[length] : schema.additionalItems;
|
|
377
|
-
const f = itemSchema ? this._toFieldConfig(itemSchema, options) : {};
|
|
378
|
-
if (f.props) {
|
|
379
|
-
f.props.required = true;
|
|
380
|
-
}
|
|
381
|
-
if (items[length]) {
|
|
382
|
-
f.props.removable = false;
|
|
383
|
-
}
|
|
384
|
-
return f;
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
|
-
break;
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
if (schema.hasOwnProperty('const')) {
|
|
391
|
-
field.props.const = schema.const;
|
|
392
|
-
this.addValidator(field, 'const', ({ value }) => value === schema.const);
|
|
393
|
-
if (!field.type) {
|
|
394
|
-
field.defaultValue = schema.const;
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
if (this.isEnum(schema)) {
|
|
398
|
-
const enumOptions = this.toEnumOptions(schema);
|
|
399
|
-
const multiple = field.type === 'array';
|
|
400
|
-
field.type = 'enum';
|
|
401
|
-
field.props.multiple = multiple;
|
|
402
|
-
field.props.options = enumOptions;
|
|
403
|
-
const enumValues = enumOptions.map((o) => o.value);
|
|
404
|
-
this.addValidator(field, 'enum', ({ value }) => {
|
|
405
|
-
if (value === undefined) {
|
|
406
|
-
return true;
|
|
407
|
-
}
|
|
408
|
-
if (multiple) {
|
|
409
|
-
return Array.isArray(value) ? value.every((o) => enumValues.includes(o)) : false;
|
|
410
|
-
}
|
|
411
|
-
return enumValues.includes(value);
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
|
-
if (schema.oneOf && !field.type) {
|
|
415
|
-
delete field.key;
|
|
416
|
-
field.fieldGroup = [
|
|
417
|
-
this.resolveMultiSchema('oneOf', schema.oneOf, { ...options, key, shareFormControl: false }),
|
|
418
|
-
];
|
|
419
|
-
}
|
|
420
|
-
if (schema.anyOf && !field.type) {
|
|
421
|
-
delete field.key;
|
|
422
|
-
field.fieldGroup = [
|
|
423
|
-
this.resolveMultiSchema('anyOf', schema.anyOf, { ...options, key, shareFormControl: false }),
|
|
424
|
-
];
|
|
425
|
-
}
|
|
426
|
-
// map in possible formlyConfig options from the widget property
|
|
427
|
-
if (schema.widget?.formlyConfig) {
|
|
428
|
-
field = this.mergeFields(field, schema.widget.formlyConfig);
|
|
429
|
-
}
|
|
430
|
-
field.templateOptions = field.props;
|
|
431
|
-
// if there is a map function passed in, use it to allow the user to
|
|
432
|
-
// further customize how fields are being mapped
|
|
433
|
-
return options.map ? options.map(field, schema) : field;
|
|
434
|
-
}
|
|
435
|
-
resolveSchema(schema, options) {
|
|
436
|
-
if (schema && schema.$ref) {
|
|
437
|
-
schema = this.resolveDefinition(schema, options);
|
|
438
|
-
}
|
|
439
|
-
if (schema && schema.allOf) {
|
|
440
|
-
schema = this.resolveAllOf(schema, options);
|
|
441
|
-
}
|
|
442
|
-
// Process if/then/else and store conditionals in options for later processing
|
|
443
|
-
if (schema && (schema.if || schema.then || schema.else)) {
|
|
444
|
-
const conditionalSchemas = this.resolveIfThenElse(schema, options);
|
|
445
|
-
if (conditionalSchemas.length > 0) {
|
|
446
|
-
// Store conditional schemas in options for processing in _toFieldConfig
|
|
447
|
-
options.conditionalSchemas = conditionalSchemas;
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
return schema;
|
|
451
|
-
}
|
|
452
|
-
resolveAllOf({ allOf, ...baseSchema }, options) {
|
|
453
|
-
if (!allOf.length) {
|
|
454
|
-
throw Error(`allOf array can not be empty ${allOf}.`);
|
|
455
|
-
}
|
|
456
|
-
return allOf.reduce((base, schema) => {
|
|
457
|
-
schema = this.resolveSchema(schema, options);
|
|
458
|
-
if (base.required && schema.required) {
|
|
459
|
-
base.required = [...base.required, ...schema.required];
|
|
460
|
-
}
|
|
461
|
-
if (schema.uniqueItems) {
|
|
462
|
-
base.uniqueItems = schema.uniqueItems;
|
|
463
|
-
}
|
|
464
|
-
// resolve to min value
|
|
465
|
-
['maxLength', 'maximum', 'exclusiveMaximum', 'maxItems', 'maxProperties'].forEach((prop) => {
|
|
466
|
-
if (!isEmpty(base[prop]) && !isEmpty(schema[prop])) {
|
|
467
|
-
base[prop] = base[prop] < schema[prop] ? base[prop] : schema[prop];
|
|
468
|
-
}
|
|
469
|
-
});
|
|
470
|
-
// resolve to max value
|
|
471
|
-
['minLength', 'minimum', 'exclusiveMinimum', 'minItems', 'minProperties'].forEach((prop) => {
|
|
472
|
-
if (!isEmpty(base[prop]) && !isEmpty(schema[prop])) {
|
|
473
|
-
base[prop] = base[prop] > schema[prop] ? base[prop] : schema[prop];
|
|
474
|
-
}
|
|
475
|
-
});
|
|
476
|
-
return _reverseDeepMerge(base, schema);
|
|
477
|
-
}, baseSchema);
|
|
478
|
-
}
|
|
479
|
-
resolveMultiSchema(mode, schemas, options) {
|
|
480
|
-
const isRecursive = this.isRecursiveMultiSchema(schemas, options);
|
|
481
|
-
const fieldGroup = schemas.map((s, i) => {
|
|
482
|
-
const expressions = {
|
|
483
|
-
hide: (f, forceUpdate) => {
|
|
484
|
-
if (options.skipMultiSchemaValidation) {
|
|
485
|
-
return true;
|
|
486
|
-
}
|
|
487
|
-
const control = f.parent.parent.fieldGroup[0].formControl;
|
|
488
|
-
if (control.value === -1 || forceUpdate) {
|
|
489
|
-
let value = f.parent.fieldGroup
|
|
490
|
-
.map((f, i) => [f, i, this.isFieldValid(f, i, schemas, options, isRecursive)])
|
|
491
|
-
.sort(([f1, i1, f1Valid], [f2, i2, f2Valid]) => {
|
|
492
|
-
if (f1Valid !== f2Valid) {
|
|
493
|
-
return f2Valid ? 1 : -1;
|
|
494
|
-
}
|
|
495
|
-
const schemaField1 = isRecursive ? this.getSchemaField(schemas[i1]) || f1 : f1;
|
|
496
|
-
const schemaField2 = isRecursive ? this.getSchemaField(schemas[i2]) || f2 : f2;
|
|
497
|
-
const matchedFields1 = totalMatchedFields(schemaField1);
|
|
498
|
-
const matchedFields2 = totalMatchedFields(schemaField2);
|
|
499
|
-
if (matchedFields1 === matchedFields2) {
|
|
500
|
-
if (schemaField1.props?.disabled === schemaField2.props?.disabled) {
|
|
501
|
-
return 0;
|
|
502
|
-
}
|
|
503
|
-
return matchedFields2 > matchedFields1 ? 1 : -1;
|
|
504
|
-
}
|
|
505
|
-
return matchedFields2 > matchedFields1 ? 1 : -1;
|
|
506
|
-
})
|
|
507
|
-
.map(([, i]) => i);
|
|
508
|
-
if (mode === 'anyOf') {
|
|
509
|
-
const definedValue = value.filter((i) => totalMatchedFields(f.parent.fieldGroup[i]));
|
|
510
|
-
value = definedValue.length > 0 ? definedValue : [value[0] || 0];
|
|
511
|
-
}
|
|
512
|
-
value = value.length > 0 ? value : [0];
|
|
513
|
-
control.setValue(mode === 'anyOf' ? value : value[0]);
|
|
514
|
-
}
|
|
515
|
-
const hide = Array.isArray(control.value) ? control.value.indexOf(i) === -1 : control.value !== i;
|
|
516
|
-
if (isRecursive && !hide) {
|
|
517
|
-
this.ensureMultiSchemaField(f, s, options);
|
|
518
|
-
}
|
|
519
|
-
return hide;
|
|
520
|
-
},
|
|
521
|
-
};
|
|
522
|
-
return isRecursive
|
|
523
|
-
? this.createMultiSchemaField(s, options, expressions)
|
|
524
|
-
: {
|
|
525
|
-
...this._toFieldConfig(s, { ...options, resetOnHide: true }),
|
|
526
|
-
expressions,
|
|
527
|
-
};
|
|
528
|
-
});
|
|
529
|
-
return {
|
|
530
|
-
type: 'multischema',
|
|
531
|
-
fieldGroup: [
|
|
532
|
-
{
|
|
533
|
-
type: 'enum',
|
|
534
|
-
defaultValue: -1,
|
|
535
|
-
props: {
|
|
536
|
-
multiple: mode === 'anyOf',
|
|
537
|
-
options: schemas.map((s, i) => ({ label: s.title, value: i, disabled: s.readOnly })),
|
|
538
|
-
},
|
|
539
|
-
hooks: {
|
|
540
|
-
onInit: (f) => f.formControl.valueChanges.pipe(tap(() => f.options.detectChanges(f.parent))),
|
|
541
|
-
},
|
|
542
|
-
},
|
|
543
|
-
{
|
|
544
|
-
fieldGroup,
|
|
545
|
-
},
|
|
546
|
-
],
|
|
547
|
-
};
|
|
548
|
-
}
|
|
549
|
-
createMultiSchemaField(schema, options, expressions) {
|
|
550
|
-
return {
|
|
551
|
-
expressions,
|
|
552
|
-
hooks: {
|
|
553
|
-
onInit: (f) => {
|
|
554
|
-
if (f.hide === false) {
|
|
555
|
-
this.ensureMultiSchemaField(f, schema, options);
|
|
556
|
-
}
|
|
557
|
-
},
|
|
558
|
-
},
|
|
559
|
-
};
|
|
560
|
-
}
|
|
561
|
-
ensureMultiSchemaField(field, schema, options) {
|
|
562
|
-
if (field._jsonSchemaFormlyField) {
|
|
563
|
-
return;
|
|
564
|
-
}
|
|
565
|
-
const expressions = field.expressions;
|
|
566
|
-
const hooks = field.hooks;
|
|
567
|
-
const mutableField = field;
|
|
568
|
-
delete mutableField['formControl'];
|
|
569
|
-
delete mutableField['_keyPath'];
|
|
570
|
-
Object.keys(field).forEach((prop) => {
|
|
571
|
-
delete mutableField[prop];
|
|
572
|
-
});
|
|
573
|
-
Object.assign(field, this._toFieldConfig(schema, { ...options, resetOnHide: true }));
|
|
574
|
-
field.expressions = expressions;
|
|
575
|
-
field.hooks = hooks;
|
|
576
|
-
Object.defineProperty(field, '_jsonSchemaFormlyField', {
|
|
577
|
-
value: true,
|
|
578
|
-
configurable: true,
|
|
579
|
-
});
|
|
580
|
-
field.options?.build(field);
|
|
581
|
-
}
|
|
582
|
-
getSchemaField(schema) {
|
|
583
|
-
return schema._field?.fieldGroup?.[0];
|
|
584
|
-
}
|
|
585
|
-
isRecursiveMultiSchema(schemas, options) {
|
|
586
|
-
const visited = new WeakSet();
|
|
587
|
-
const visit = (schema) => {
|
|
588
|
-
if (!schema || typeof schema !== 'object') {
|
|
589
|
-
return false;
|
|
590
|
-
}
|
|
591
|
-
if (visited.has(schema)) {
|
|
592
|
-
return false;
|
|
593
|
-
}
|
|
594
|
-
visited.add(schema);
|
|
595
|
-
if (schema.$ref) {
|
|
596
|
-
const definition = this.getDefinition(schema.$ref, options);
|
|
597
|
-
return !!definition && (definition.oneOf === schemas || definition.anyOf === schemas || visit(definition));
|
|
598
|
-
}
|
|
599
|
-
if (schema.oneOf === schemas || schema.anyOf === schemas) {
|
|
600
|
-
return true;
|
|
601
|
-
}
|
|
602
|
-
const children = [];
|
|
603
|
-
if (schema.properties) {
|
|
604
|
-
children.push(...Object.values(schema.properties));
|
|
605
|
-
}
|
|
606
|
-
if (schema.items) {
|
|
607
|
-
children.push(...(Array.isArray(schema.items) ? schema.items : [schema.items]));
|
|
608
|
-
}
|
|
609
|
-
if (schema.additionalItems && typeof schema.additionalItems === 'object') {
|
|
610
|
-
children.push(schema.additionalItems);
|
|
611
|
-
}
|
|
612
|
-
if (schema.allOf) {
|
|
613
|
-
children.push(...schema.allOf);
|
|
614
|
-
}
|
|
615
|
-
if (schema.oneOf && schema.oneOf !== schemas) {
|
|
616
|
-
children.push(...schema.oneOf);
|
|
617
|
-
}
|
|
618
|
-
if (schema.anyOf && schema.anyOf !== schemas) {
|
|
619
|
-
children.push(...schema.anyOf);
|
|
620
|
-
}
|
|
621
|
-
if (schema.dependencies) {
|
|
622
|
-
Object.values(schema.dependencies).forEach((dependency) => {
|
|
623
|
-
if (dependency && !Array.isArray(dependency)) {
|
|
624
|
-
children.push(dependency);
|
|
625
|
-
}
|
|
626
|
-
});
|
|
627
|
-
}
|
|
628
|
-
['not', 'if', 'then', 'else'].forEach((key) => {
|
|
629
|
-
const child = schema[key];
|
|
630
|
-
if (child && typeof child === 'object') {
|
|
631
|
-
children.push(child);
|
|
632
|
-
}
|
|
633
|
-
});
|
|
634
|
-
return children.some(visit);
|
|
635
|
-
};
|
|
636
|
-
return schemas.some(visit);
|
|
637
|
-
}
|
|
638
|
-
getDefinition(ref, options) {
|
|
639
|
-
const [uri, pointer] = ref.split('#/');
|
|
640
|
-
if (uri || !pointer) {
|
|
641
|
-
return null;
|
|
642
|
-
}
|
|
643
|
-
let definition = options.schema;
|
|
644
|
-
for (const path of pointer.split('/')) {
|
|
645
|
-
if (!definition || typeof definition !== 'object' || !definition.hasOwnProperty(path)) {
|
|
646
|
-
return null;
|
|
647
|
-
}
|
|
648
|
-
definition = definition[path];
|
|
649
|
-
}
|
|
650
|
-
return typeof definition === 'object' ? definition : null;
|
|
651
|
-
}
|
|
652
|
-
resolveDefinition(schema, options) {
|
|
653
|
-
const [uri, pointer] = schema.$ref.split('#/');
|
|
654
|
-
if (uri) {
|
|
655
|
-
throw Error(`Remote schemas for ${schema.$ref} not supported yet.`);
|
|
656
|
-
}
|
|
657
|
-
const definition = !pointer
|
|
658
|
-
? null
|
|
659
|
-
: pointer
|
|
660
|
-
.split('/')
|
|
661
|
-
.reduce((def, path) => (def?.hasOwnProperty(path) ? def[path] : null), options.schema);
|
|
662
|
-
if (!definition) {
|
|
663
|
-
throw Error(`Cannot find a definition for ${schema.$ref}.`);
|
|
664
|
-
}
|
|
665
|
-
if (definition.$ref) {
|
|
666
|
-
return this.resolveDefinition(definition, options);
|
|
667
|
-
}
|
|
668
|
-
return {
|
|
669
|
-
...definition,
|
|
670
|
-
...['title', 'description', 'default', 'widget'].reduce((annotation, p) => {
|
|
671
|
-
if (schema.hasOwnProperty(p)) {
|
|
672
|
-
annotation[p] = schema[p];
|
|
673
|
-
}
|
|
674
|
-
return annotation;
|
|
675
|
-
}, {}),
|
|
676
|
-
};
|
|
677
|
-
}
|
|
678
|
-
resolveDependencies(schema) {
|
|
679
|
-
const propDeps = {};
|
|
680
|
-
const schemaDeps = {};
|
|
681
|
-
Object.keys(schema.dependencies || {}).forEach((prop) => {
|
|
682
|
-
const dependency = schema.dependencies[prop];
|
|
683
|
-
if (Array.isArray(dependency)) {
|
|
684
|
-
// Property dependencies
|
|
685
|
-
dependency.forEach((dep) => {
|
|
686
|
-
if (!propDeps[dep]) {
|
|
687
|
-
propDeps[dep] = [prop];
|
|
688
|
-
}
|
|
689
|
-
else {
|
|
690
|
-
propDeps[dep].push(prop);
|
|
691
|
-
}
|
|
692
|
-
});
|
|
693
|
-
}
|
|
694
|
-
else {
|
|
695
|
-
// schema dependencies
|
|
696
|
-
schemaDeps[prop] = dependency;
|
|
697
|
-
}
|
|
698
|
-
});
|
|
699
|
-
return { propDeps, schemaDeps };
|
|
700
|
-
}
|
|
701
|
-
extractIfCondition(ifSchema) {
|
|
702
|
-
// Extract the property and const value from the "if" schema
|
|
703
|
-
// Supports: { "properties": { "propName": { "const": value } } }
|
|
704
|
-
if (ifSchema.properties) {
|
|
705
|
-
const propName = Object.keys(ifSchema.properties)[0];
|
|
706
|
-
if (propName) {
|
|
707
|
-
const propSchema = ifSchema.properties[propName];
|
|
708
|
-
if (propSchema && propSchema.hasOwnProperty('const')) {
|
|
709
|
-
return { property: propName, value: propSchema.const };
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
return null;
|
|
714
|
-
}
|
|
715
|
-
resolveIfThenElse(schema, options) {
|
|
716
|
-
// Process if/then/else and return array of conditional schemas to add to fieldGroup
|
|
717
|
-
const conditionalSchemas = [];
|
|
718
|
-
if (schema.if && typeof schema.if === 'object') {
|
|
719
|
-
const condition = this.extractIfCondition(schema.if);
|
|
720
|
-
if (condition) {
|
|
721
|
-
// Process "then" branch
|
|
722
|
-
if (schema.then && typeof schema.then === 'object') {
|
|
723
|
-
const resolvedSchema = this.resolveConditionalSchema(schema.then, options);
|
|
724
|
-
conditionalSchemas.push({
|
|
725
|
-
...resolvedSchema,
|
|
726
|
-
_ifCondition: condition,
|
|
727
|
-
});
|
|
728
|
-
}
|
|
729
|
-
// Process "else" branch
|
|
730
|
-
if (schema.else && typeof schema.else === 'object') {
|
|
731
|
-
const resolvedSchema = this.resolveConditionalSchema(schema.else, options);
|
|
732
|
-
conditionalSchemas.push({
|
|
733
|
-
...resolvedSchema,
|
|
734
|
-
_ifCondition: { property: condition.property, value: condition.value, negate: true },
|
|
735
|
-
});
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
return conditionalSchemas;
|
|
740
|
-
}
|
|
741
|
-
resolveConditionalSchema(conditionalSchema, options) {
|
|
742
|
-
// Resolve $ref and allOf if present in conditional schema
|
|
743
|
-
// Note: We don't use resolveSchema here to avoid recursive processing of nested if/then/else
|
|
744
|
-
let resolved = conditionalSchema;
|
|
745
|
-
if (conditionalSchema.$ref) {
|
|
746
|
-
resolved = this.resolveDefinition(conditionalSchema, options);
|
|
747
|
-
}
|
|
748
|
-
if (resolved.allOf) {
|
|
749
|
-
resolved = this.resolveAllOf(resolved, options);
|
|
750
|
-
}
|
|
751
|
-
return resolved;
|
|
752
|
-
}
|
|
753
|
-
guessSchemaType(schema) {
|
|
754
|
-
const type = schema?.type;
|
|
755
|
-
if (!type && schema?.properties) {
|
|
756
|
-
return ['object'];
|
|
757
|
-
}
|
|
758
|
-
if (Array.isArray(type)) {
|
|
759
|
-
if (type.length === 1) {
|
|
760
|
-
return type;
|
|
761
|
-
}
|
|
762
|
-
if (type.length === 2 && type.indexOf('null') !== -1) {
|
|
763
|
-
return type.sort((t1) => (t1 == 'null' ? 1 : -1));
|
|
764
|
-
}
|
|
765
|
-
return type;
|
|
766
|
-
}
|
|
767
|
-
return type ? [type] : [];
|
|
768
|
-
}
|
|
769
|
-
addValidator(field, name, validator) {
|
|
770
|
-
field.validators = field.validators || {};
|
|
771
|
-
field.validators[name] = validator;
|
|
772
|
-
}
|
|
773
|
-
isEnum(schema) {
|
|
774
|
-
return (!!schema.enum ||
|
|
775
|
-
(schema.anyOf && schema.anyOf.every(isConst)) ||
|
|
776
|
-
(schema.oneOf && schema.oneOf.every(isConst)) ||
|
|
777
|
-
(schema.uniqueItems && schema.items && !Array.isArray(schema.items) && this.isEnum(schema.items)));
|
|
778
|
-
}
|
|
779
|
-
toEnumOptions(schema) {
|
|
780
|
-
if (schema.enum) {
|
|
781
|
-
return schema.enum.map((value) => ({ value, label: value }));
|
|
782
|
-
}
|
|
783
|
-
const toEnum = (s) => {
|
|
784
|
-
const value = s.hasOwnProperty('const') ? s.const : s.enum[0];
|
|
785
|
-
const option = { value, label: s.title || value };
|
|
786
|
-
if (s.readOnly) {
|
|
787
|
-
option.disabled = true;
|
|
788
|
-
}
|
|
789
|
-
return option;
|
|
790
|
-
};
|
|
791
|
-
if (schema.anyOf) {
|
|
792
|
-
return schema.anyOf.map(toEnum);
|
|
793
|
-
}
|
|
794
|
-
if (schema.oneOf) {
|
|
795
|
-
return schema.oneOf.map(toEnum);
|
|
796
|
-
}
|
|
797
|
-
return this.toEnumOptions(schema.items);
|
|
798
|
-
}
|
|
799
|
-
isFieldValid(root, i, schemas, options, skipMultiSchemaValidation = false) {
|
|
800
|
-
const schema = schemas[i];
|
|
801
|
-
if (!schema._field) {
|
|
802
|
-
Object.defineProperty(schema, '_field', { enumerable: false, writable: true, configurable: true });
|
|
803
|
-
}
|
|
804
|
-
let field = schema._field;
|
|
805
|
-
let model = root.model ? root.model : root.fieldArray ? [] : {};
|
|
806
|
-
if (root.model && _hasKey(root)) {
|
|
807
|
-
model = { [Array.isArray(root.key) ? root.key.join('.') : root.key]: _getFieldValue(root) };
|
|
808
|
-
}
|
|
809
|
-
model = _clone(model);
|
|
810
|
-
if (!field) {
|
|
811
|
-
field = schema._field = root.options.build({
|
|
812
|
-
form: Array.isArray(model) ? new UntypedFormArray([]) : new UntypedFormGroup({}),
|
|
813
|
-
fieldGroup: [
|
|
814
|
-
this._toFieldConfig(schema, {
|
|
815
|
-
...options,
|
|
816
|
-
resetOnHide: true,
|
|
817
|
-
ignoreDefault: true,
|
|
818
|
-
skipMultiSchemaValidation,
|
|
819
|
-
map: null,
|
|
820
|
-
}),
|
|
821
|
-
],
|
|
822
|
-
model,
|
|
823
|
-
options: {},
|
|
824
|
-
});
|
|
825
|
-
}
|
|
826
|
-
else {
|
|
827
|
-
field.model = model;
|
|
828
|
-
root.options.build(field);
|
|
829
|
-
}
|
|
830
|
-
return field.form.valid;
|
|
831
|
-
}
|
|
832
|
-
mergeFields(f1, f2) {
|
|
833
|
-
for (const prop in f2) {
|
|
834
|
-
const f1Prop = prop === 'templateOptions' ? 'props' : prop;
|
|
835
|
-
if (isObject(f1[f1Prop]) && isObject(f2[prop])) {
|
|
836
|
-
f1[f1Prop] = this.mergeFields(f1[f1Prop], f2[prop]);
|
|
837
|
-
}
|
|
838
|
-
else if (f2[prop] != null) {
|
|
839
|
-
f1[f1Prop] = f2[prop];
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
return f1;
|
|
843
|
-
}
|
|
844
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: FormlyJsonschema, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
845
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: FormlyJsonschema, providedIn: 'root' }); }
|
|
35
|
+
if (!field.fieldGroup) return ɵhasKey(field) && ɵgetFieldValue(field) !== void 0 ? 1 : 0;
|
|
36
|
+
const total = field.fieldGroup.reduce((s, f) => totalMatchedFields(f) + s, 0);
|
|
37
|
+
if (total === 0 && ɵhasKey(field)) {
|
|
38
|
+
const value = ɵgetFieldValue(field);
|
|
39
|
+
if (value === null || value !== void 0 && (field.fieldArray && Array.isArray(value) || !field.fieldArray && isObject(value))) return 1;
|
|
40
|
+
}
|
|
41
|
+
return total;
|
|
846
42
|
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
43
|
+
var FormlyJsonschema = class FormlyJsonschema {
|
|
44
|
+
toFieldConfig(schema, options) {
|
|
45
|
+
schema = ɵclone(schema);
|
|
46
|
+
return this._toFieldConfig(schema, {
|
|
47
|
+
schema,
|
|
48
|
+
...options || {}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
_toFieldConfig(schema, { key, isOptional, ...options }) {
|
|
52
|
+
schema = this.resolveSchema(schema, options);
|
|
53
|
+
const types = this.guessSchemaType(schema);
|
|
54
|
+
let field = {
|
|
55
|
+
type: types[0],
|
|
56
|
+
defaultValue: schema.default,
|
|
57
|
+
props: {
|
|
58
|
+
label: schema.title,
|
|
59
|
+
readonly: schema.readOnly,
|
|
60
|
+
description: schema.description
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
if (key != null) field.key = key;
|
|
64
|
+
if (!options.ignoreDefault && (schema.readOnly || options.readOnly)) {
|
|
65
|
+
field.props.disabled = true;
|
|
66
|
+
options = {
|
|
67
|
+
...options,
|
|
68
|
+
readOnly: true
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (options.resetOnHide) field.resetOnHide = true;
|
|
72
|
+
if (options.shareFormControl === false) field.shareFormControl = false;
|
|
73
|
+
if (field.defaultValue === void 0 && types.length === 1 && isOptional === false) switch (types[0]) {
|
|
74
|
+
case "null":
|
|
75
|
+
field.defaultValue = null;
|
|
76
|
+
break;
|
|
77
|
+
case "string":
|
|
78
|
+
if (!options.ignoreImplicitStringDefaults) field.defaultValue = "";
|
|
79
|
+
break;
|
|
80
|
+
case "object":
|
|
81
|
+
field.defaultValue = {};
|
|
82
|
+
break;
|
|
83
|
+
case "array": field.defaultValue = schema.minItems > 0 ? Array.from(new Array(schema.minItems)) : [];
|
|
84
|
+
}
|
|
85
|
+
if (options.ignoreDefault) delete field.defaultValue;
|
|
86
|
+
this.addValidator(field, "type", {
|
|
87
|
+
schemaType: types,
|
|
88
|
+
expression: ({ value }) => {
|
|
89
|
+
if (value === void 0) return true;
|
|
90
|
+
if (value === null && types.indexOf("null") !== -1) return true;
|
|
91
|
+
switch (types[0]) {
|
|
92
|
+
case "null": return false;
|
|
93
|
+
case "string": return typeof value === "string";
|
|
94
|
+
case "integer": return isInteger(value);
|
|
95
|
+
case "number": return typeof value === "number";
|
|
96
|
+
case "object": return isObject(value);
|
|
97
|
+
case "array": return Array.isArray(value);
|
|
98
|
+
}
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
switch (field.type) {
|
|
103
|
+
case "number":
|
|
104
|
+
case "integer":
|
|
105
|
+
field.parsers = [(v, f) => {
|
|
106
|
+
v = v === "" && types.includes("null") ? null : toNumber(v);
|
|
107
|
+
if (v === null && f) {
|
|
108
|
+
const input = typeof document !== "undefined" && f.id ? document.querySelector(`#${f.id}`) : void 0;
|
|
109
|
+
if (input && input.validity && !input.validity.badInput && !types.includes("null")) v = void 0;
|
|
110
|
+
if (v !== f.formControl.value) f.formControl.setValue(v, { emitModelToViewChange: false });
|
|
111
|
+
}
|
|
112
|
+
return v;
|
|
113
|
+
}];
|
|
114
|
+
if (schema.hasOwnProperty("minimum")) field.props.min = schema.minimum;
|
|
115
|
+
if (schema.hasOwnProperty("maximum")) field.props.max = schema.maximum;
|
|
116
|
+
if (schema.hasOwnProperty("exclusiveMinimum")) {
|
|
117
|
+
field.props.exclusiveMinimum = schema.exclusiveMinimum;
|
|
118
|
+
this.addValidator(field, "exclusiveMinimum", ({ value }) => isEmpty(value) || value > schema.exclusiveMinimum);
|
|
119
|
+
}
|
|
120
|
+
if (schema.hasOwnProperty("exclusiveMaximum")) {
|
|
121
|
+
field.props.exclusiveMaximum = schema.exclusiveMaximum;
|
|
122
|
+
this.addValidator(field, "exclusiveMaximum", ({ value }) => isEmpty(value) || value < schema.exclusiveMaximum);
|
|
123
|
+
}
|
|
124
|
+
if (schema.hasOwnProperty("multipleOf")) {
|
|
125
|
+
field.props.step = schema.multipleOf;
|
|
126
|
+
this.addValidator(field, "multipleOf", ({ value }) => {
|
|
127
|
+
if (isEmpty(value) || typeof value !== "number" || value === 0 || schema.multipleOf <= 0) return true;
|
|
128
|
+
const multiplier = Math.pow(10, decimalPlaces(schema.multipleOf));
|
|
129
|
+
return Math.round(value * multiplier) % Math.round(schema.multipleOf * multiplier) === 0;
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
case "string":
|
|
134
|
+
field.parsers = [(v, f) => {
|
|
135
|
+
if (types.indexOf("null") !== -1) v = isEmpty(v) ? null : v;
|
|
136
|
+
else if (f && !f.props.required) v = v === "" ? void 0 : v;
|
|
137
|
+
return v;
|
|
138
|
+
}];
|
|
139
|
+
[
|
|
140
|
+
"minLength",
|
|
141
|
+
"maxLength",
|
|
142
|
+
"pattern"
|
|
143
|
+
].forEach((prop) => {
|
|
144
|
+
if (schema.hasOwnProperty(prop)) field.props[prop] = schema[prop];
|
|
145
|
+
});
|
|
146
|
+
break;
|
|
147
|
+
case "object": {
|
|
148
|
+
if (!field.fieldGroup) field.fieldGroup = [];
|
|
149
|
+
const { propDeps, schemaDeps } = this.resolveDependencies(schema);
|
|
150
|
+
Object.keys(schema.properties || {}).forEach((property) => {
|
|
151
|
+
const isRequired = Array.isArray(schema.required) && schema.required.indexOf(property) !== -1;
|
|
152
|
+
const f = this._toFieldConfig(schema.properties[property], {
|
|
153
|
+
...options,
|
|
154
|
+
key: property,
|
|
155
|
+
isOptional: isOptional || !isRequired
|
|
156
|
+
});
|
|
157
|
+
field.fieldGroup.push(f);
|
|
158
|
+
if (isRequired || propDeps[property]) f.expressions = {
|
|
159
|
+
...f.expressions || {},
|
|
160
|
+
"props.required": (f) => {
|
|
161
|
+
let parent = f.parent;
|
|
162
|
+
const model = f.fieldGroup && f.key != null ? parent.model : f.model;
|
|
163
|
+
while (parent.key == null && parent.parent) parent = parent.parent;
|
|
164
|
+
const required = parent && parent.props ? parent.props.required : false;
|
|
165
|
+
if (!model && !required) return false;
|
|
166
|
+
if (Array.isArray(schema.required) && schema.required.indexOf(property) !== -1) return true;
|
|
167
|
+
return propDeps[property] && f.model && propDeps[property].some((k) => !isEmpty(f.model[k]));
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
if (schemaDeps[property]) {
|
|
171
|
+
const getConstValue = (s) => {
|
|
172
|
+
return s.hasOwnProperty("const") ? s.const : s.enum[0];
|
|
173
|
+
};
|
|
174
|
+
const oneOfSchema = schemaDeps[property].oneOf;
|
|
175
|
+
if (oneOfSchema && oneOfSchema.every((o) => o.properties && o.properties[property] && isConst(o.properties[property]))) oneOfSchema.forEach((oneOfSchemaItem) => {
|
|
176
|
+
const { [property]: constSchema, ...properties } = oneOfSchemaItem.properties;
|
|
177
|
+
field.fieldGroup.push({
|
|
178
|
+
...this._toFieldConfig({
|
|
179
|
+
...oneOfSchemaItem,
|
|
180
|
+
properties
|
|
181
|
+
}, {
|
|
182
|
+
...options,
|
|
183
|
+
shareFormControl: false,
|
|
184
|
+
resetOnHide: true
|
|
185
|
+
}),
|
|
186
|
+
expressions: { hide: (f) => !f.model || getConstValue(constSchema) !== f.model[property] }
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
else field.fieldGroup.push({
|
|
190
|
+
...this._toFieldConfig(schemaDeps[property], options),
|
|
191
|
+
expressions: { hide: (f) => !f.model || isEmpty(f.model[property]) }
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
if (schema.oneOf) field.fieldGroup.push(this.resolveMultiSchema("oneOf", schema.oneOf, {
|
|
196
|
+
...options,
|
|
197
|
+
shareFormControl: false
|
|
198
|
+
}));
|
|
199
|
+
if (schema.anyOf) field.fieldGroup.push(this.resolveMultiSchema("anyOf", schema.anyOf, options));
|
|
200
|
+
if (options.conditionalSchemas) options.conditionalSchemas.forEach((conditionalSchema) => {
|
|
201
|
+
const condition = conditionalSchema._ifCondition;
|
|
202
|
+
if (condition && conditionalSchema.properties) {
|
|
203
|
+
const conditionalFieldGroup = {
|
|
204
|
+
fieldGroup: [],
|
|
205
|
+
expressions: { hide: (f) => {
|
|
206
|
+
if (!f.model) return true;
|
|
207
|
+
const matches = f.model[condition.property] === condition.value;
|
|
208
|
+
return condition.negate ? matches : !matches;
|
|
209
|
+
} }
|
|
210
|
+
};
|
|
211
|
+
Object.keys(conditionalSchema.properties).forEach((property) => {
|
|
212
|
+
const propSchema = conditionalSchema.properties[property];
|
|
213
|
+
if (!propSchema) return;
|
|
214
|
+
const isRequired = Array.isArray(conditionalSchema.required) && conditionalSchema.required.indexOf(property) !== -1;
|
|
215
|
+
const conditionalField = this._toFieldConfig(propSchema, {
|
|
216
|
+
...options,
|
|
217
|
+
key: property,
|
|
218
|
+
isOptional: !isRequired,
|
|
219
|
+
resetOnHide: true
|
|
220
|
+
});
|
|
221
|
+
conditionalFieldGroup.fieldGroup.push(conditionalField);
|
|
222
|
+
});
|
|
223
|
+
field.fieldGroup.push(conditionalFieldGroup);
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
case "array":
|
|
229
|
+
if (schema.hasOwnProperty("minItems")) {
|
|
230
|
+
field.props.minItems = schema.minItems;
|
|
231
|
+
this.addValidator(field, "minItems", ({ value }) => {
|
|
232
|
+
return isEmpty(value) || value.length >= schema.minItems;
|
|
233
|
+
});
|
|
234
|
+
if (!isOptional && schema.minItems > 0 && field.defaultValue === void 0) field.defaultValue = Array.from(new Array(schema.minItems));
|
|
235
|
+
}
|
|
236
|
+
if (schema.hasOwnProperty("maxItems")) {
|
|
237
|
+
field.props.maxItems = schema.maxItems;
|
|
238
|
+
this.addValidator(field, "maxItems", ({ value }) => {
|
|
239
|
+
return isEmpty(value) || value.length <= schema.maxItems;
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
if (schema.hasOwnProperty("uniqueItems")) {
|
|
243
|
+
field.props.uniqueItems = schema.uniqueItems;
|
|
244
|
+
this.addValidator(field, "uniqueItems", ({ value }) => {
|
|
245
|
+
if (isEmpty(value) || !schema.uniqueItems) return true;
|
|
246
|
+
return Array.from(new Set(value.map((v) => JSON.stringify(v, (k, o) => {
|
|
247
|
+
if (isObject(o)) return Object.keys(o).sort().reduce((obj, key) => {
|
|
248
|
+
obj[key] = o[key];
|
|
249
|
+
return obj;
|
|
250
|
+
}, {});
|
|
251
|
+
return o;
|
|
252
|
+
})))).length === value.length;
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
if (schema.items && !Array.isArray(schema.items)) schema.items = this.resolveSchema(schema.items, options);
|
|
256
|
+
if (!this.isEnum(schema)) {
|
|
257
|
+
if (Array.isArray(schema.items)) field.props.trackByIndex = true;
|
|
258
|
+
field.fieldArray = (root) => {
|
|
259
|
+
const length = root.fieldGroup ? root.fieldGroup.length : 0;
|
|
260
|
+
const items = schema.items;
|
|
261
|
+
if (!Array.isArray(items)) {
|
|
262
|
+
if (!items) return {};
|
|
263
|
+
const isMultiSchema = items.oneOf || items.anyOf;
|
|
264
|
+
const f = this._toFieldConfig(items, isMultiSchema ? {
|
|
265
|
+
...options,
|
|
266
|
+
key: `${length}`,
|
|
267
|
+
isOptional: false
|
|
268
|
+
} : {
|
|
269
|
+
...options,
|
|
270
|
+
isOptional: false
|
|
271
|
+
});
|
|
272
|
+
if (isMultiSchema && !ɵhasKey(f)) f.key = null;
|
|
273
|
+
return f;
|
|
274
|
+
}
|
|
275
|
+
const itemSchema = items[length] ? items[length] : schema.additionalItems;
|
|
276
|
+
const f = itemSchema ? this._toFieldConfig(itemSchema, options) : {};
|
|
277
|
+
if (f.props) f.props.required = true;
|
|
278
|
+
if (items[length]) f.props.removable = false;
|
|
279
|
+
return f;
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
if (schema.hasOwnProperty("const")) {
|
|
284
|
+
field.props.const = schema.const;
|
|
285
|
+
this.addValidator(field, "const", ({ value }) => value === schema.const);
|
|
286
|
+
if (!field.type) field.defaultValue = schema.const;
|
|
287
|
+
}
|
|
288
|
+
if (this.isEnum(schema)) {
|
|
289
|
+
const enumOptions = this.toEnumOptions(schema);
|
|
290
|
+
const multiple = field.type === "array";
|
|
291
|
+
field.type = "enum";
|
|
292
|
+
field.props.multiple = multiple;
|
|
293
|
+
field.props.options = enumOptions;
|
|
294
|
+
const enumValues = enumOptions.map((o) => o.value);
|
|
295
|
+
this.addValidator(field, "enum", ({ value }) => {
|
|
296
|
+
if (value === void 0) return true;
|
|
297
|
+
if (multiple) return Array.isArray(value) ? value.every((o) => enumValues.includes(o)) : false;
|
|
298
|
+
return enumValues.includes(value);
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
if (schema.oneOf && !field.type) {
|
|
302
|
+
delete field.key;
|
|
303
|
+
field.fieldGroup = [this.resolveMultiSchema("oneOf", schema.oneOf, {
|
|
304
|
+
...options,
|
|
305
|
+
key,
|
|
306
|
+
shareFormControl: false
|
|
307
|
+
})];
|
|
308
|
+
}
|
|
309
|
+
if (schema.anyOf && !field.type) {
|
|
310
|
+
delete field.key;
|
|
311
|
+
field.fieldGroup = [this.resolveMultiSchema("anyOf", schema.anyOf, {
|
|
312
|
+
...options,
|
|
313
|
+
key,
|
|
314
|
+
shareFormControl: false
|
|
315
|
+
})];
|
|
316
|
+
}
|
|
317
|
+
if (schema.widget?.formlyConfig) field = this.mergeFields(field, schema.widget.formlyConfig);
|
|
318
|
+
field.templateOptions = field.props;
|
|
319
|
+
return options.map ? options.map(field, schema) : field;
|
|
320
|
+
}
|
|
321
|
+
resolveSchema(schema, options) {
|
|
322
|
+
if (schema && schema.$ref) schema = this.resolveDefinition(schema, options);
|
|
323
|
+
if (schema && schema.allOf) schema = this.resolveAllOf(schema, options);
|
|
324
|
+
if (schema && (schema.if || schema.then || schema.else)) {
|
|
325
|
+
const conditionalSchemas = this.resolveIfThenElse(schema, options);
|
|
326
|
+
if (conditionalSchemas.length > 0) options.conditionalSchemas = conditionalSchemas;
|
|
327
|
+
}
|
|
328
|
+
return schema;
|
|
329
|
+
}
|
|
330
|
+
resolveAllOf({ allOf, ...baseSchema }, options) {
|
|
331
|
+
if (!allOf.length) throw Error(`allOf array can not be empty ${allOf}.`);
|
|
332
|
+
return allOf.reduce((base, schema) => {
|
|
333
|
+
schema = this.resolveSchema(schema, options);
|
|
334
|
+
if (base.required && schema.required) base.required = [...base.required, ...schema.required];
|
|
335
|
+
if (schema.uniqueItems) base.uniqueItems = schema.uniqueItems;
|
|
336
|
+
[
|
|
337
|
+
"maxLength",
|
|
338
|
+
"maximum",
|
|
339
|
+
"exclusiveMaximum",
|
|
340
|
+
"maxItems",
|
|
341
|
+
"maxProperties"
|
|
342
|
+
].forEach((prop) => {
|
|
343
|
+
if (!isEmpty(base[prop]) && !isEmpty(schema[prop])) base[prop] = base[prop] < schema[prop] ? base[prop] : schema[prop];
|
|
344
|
+
});
|
|
345
|
+
[
|
|
346
|
+
"minLength",
|
|
347
|
+
"minimum",
|
|
348
|
+
"exclusiveMinimum",
|
|
349
|
+
"minItems",
|
|
350
|
+
"minProperties"
|
|
351
|
+
].forEach((prop) => {
|
|
352
|
+
if (!isEmpty(base[prop]) && !isEmpty(schema[prop])) base[prop] = base[prop] > schema[prop] ? base[prop] : schema[prop];
|
|
353
|
+
});
|
|
354
|
+
return ɵreverseDeepMerge(base, schema);
|
|
355
|
+
}, baseSchema);
|
|
356
|
+
}
|
|
357
|
+
resolveMultiSchema(mode, schemas, options) {
|
|
358
|
+
const isRecursive = this.isRecursiveMultiSchema(schemas, options);
|
|
359
|
+
const fieldGroup = schemas.map((s, i) => {
|
|
360
|
+
const expressions = { hide: (f, forceUpdate) => {
|
|
361
|
+
if (options.skipMultiSchemaValidation) return true;
|
|
362
|
+
const control = f.parent.parent.fieldGroup[0].formControl;
|
|
363
|
+
if (control.value === -1 || forceUpdate) {
|
|
364
|
+
let value = f.parent.fieldGroup.map((f, i) => [
|
|
365
|
+
f,
|
|
366
|
+
i,
|
|
367
|
+
this.isFieldValid(f, i, schemas, options, isRecursive)
|
|
368
|
+
]).sort(([f1, i1, f1Valid], [f2, i2, f2Valid]) => {
|
|
369
|
+
if (f1Valid !== f2Valid) return f2Valid ? 1 : -1;
|
|
370
|
+
const schemaField1 = isRecursive ? this.getSchemaField(schemas[i1]) || f1 : f1;
|
|
371
|
+
const schemaField2 = isRecursive ? this.getSchemaField(schemas[i2]) || f2 : f2;
|
|
372
|
+
const matchedFields1 = totalMatchedFields(schemaField1);
|
|
373
|
+
const matchedFields2 = totalMatchedFields(schemaField2);
|
|
374
|
+
if (matchedFields1 === matchedFields2) {
|
|
375
|
+
if (schemaField1.props?.disabled === schemaField2.props?.disabled) return 0;
|
|
376
|
+
return matchedFields2 > matchedFields1 ? 1 : -1;
|
|
377
|
+
}
|
|
378
|
+
return matchedFields2 > matchedFields1 ? 1 : -1;
|
|
379
|
+
}).map(([, i]) => i);
|
|
380
|
+
if (mode === "anyOf") {
|
|
381
|
+
const definedValue = value.filter((i) => totalMatchedFields(f.parent.fieldGroup[i]));
|
|
382
|
+
value = definedValue.length > 0 ? definedValue : [value[0] || 0];
|
|
383
|
+
}
|
|
384
|
+
value = value.length > 0 ? value : [0];
|
|
385
|
+
control.setValue(mode === "anyOf" ? value : value[0]);
|
|
386
|
+
}
|
|
387
|
+
const hide = Array.isArray(control.value) ? control.value.indexOf(i) === -1 : control.value !== i;
|
|
388
|
+
if (isRecursive && !hide) this.ensureMultiSchemaField(f, s, options);
|
|
389
|
+
return hide;
|
|
390
|
+
} };
|
|
391
|
+
return isRecursive ? this.createMultiSchemaField(s, options, expressions) : {
|
|
392
|
+
...this._toFieldConfig(s, {
|
|
393
|
+
...options,
|
|
394
|
+
resetOnHide: true
|
|
395
|
+
}),
|
|
396
|
+
expressions
|
|
397
|
+
};
|
|
398
|
+
});
|
|
399
|
+
return {
|
|
400
|
+
type: "multischema",
|
|
401
|
+
fieldGroup: [{
|
|
402
|
+
type: "enum",
|
|
403
|
+
defaultValue: -1,
|
|
404
|
+
props: {
|
|
405
|
+
multiple: mode === "anyOf",
|
|
406
|
+
options: schemas.map((s, i) => ({
|
|
407
|
+
label: s.title,
|
|
408
|
+
value: i,
|
|
409
|
+
disabled: s.readOnly
|
|
410
|
+
}))
|
|
411
|
+
},
|
|
412
|
+
hooks: { onInit: (f) => f.formControl.valueChanges.pipe(tap(() => f.options.detectChanges(f.parent))) }
|
|
413
|
+
}, { fieldGroup }]
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
createMultiSchemaField(schema, options, expressions) {
|
|
417
|
+
return {
|
|
418
|
+
key: options.key,
|
|
419
|
+
shareFormControl: options.shareFormControl,
|
|
420
|
+
expressions,
|
|
421
|
+
hooks: { onInit: (f) => {
|
|
422
|
+
if (f.hide === false) this.ensureMultiSchemaField(f, schema, options);
|
|
423
|
+
} }
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
ensureMultiSchemaField(field, schema, options) {
|
|
427
|
+
if (field._jsonSchemaFormlyField) return;
|
|
428
|
+
const key = field.key;
|
|
429
|
+
const expressions = field.expressions;
|
|
430
|
+
const hooks = field.hooks;
|
|
431
|
+
const mutableField = field;
|
|
432
|
+
delete mutableField["formControl"];
|
|
433
|
+
delete mutableField["_keyPath"];
|
|
434
|
+
Object.keys(field).forEach((prop) => {
|
|
435
|
+
if (prop !== "hide") delete mutableField[prop];
|
|
436
|
+
});
|
|
437
|
+
Object.assign(field, this._toFieldConfig(schema, {
|
|
438
|
+
...options,
|
|
439
|
+
key,
|
|
440
|
+
resetOnHide: true
|
|
441
|
+
}));
|
|
442
|
+
field.expressions = expressions;
|
|
443
|
+
field.hooks = hooks;
|
|
444
|
+
Object.defineProperty(field, "_jsonSchemaFormlyField", {
|
|
445
|
+
value: true,
|
|
446
|
+
configurable: true
|
|
447
|
+
});
|
|
448
|
+
field.options?.build(field);
|
|
449
|
+
}
|
|
450
|
+
getSchemaField(schema) {
|
|
451
|
+
return schema._field?.fieldGroup?.[0];
|
|
452
|
+
}
|
|
453
|
+
isRecursiveMultiSchema(schemas, options) {
|
|
454
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
455
|
+
const visit = (schema) => {
|
|
456
|
+
if (!schema || typeof schema !== "object") return false;
|
|
457
|
+
if (visited.has(schema)) return false;
|
|
458
|
+
visited.add(schema);
|
|
459
|
+
if (schema.$ref) {
|
|
460
|
+
const definition = this.getDefinition(schema.$ref, options);
|
|
461
|
+
return !!definition && (definition.oneOf === schemas || definition.anyOf === schemas || visit(definition));
|
|
462
|
+
}
|
|
463
|
+
if (schema.oneOf === schemas || schema.anyOf === schemas) return true;
|
|
464
|
+
const children = [];
|
|
465
|
+
if (schema.properties) children.push(...Object.values(schema.properties));
|
|
466
|
+
if (schema.items) children.push(...Array.isArray(schema.items) ? schema.items : [schema.items]);
|
|
467
|
+
if (schema.additionalItems && typeof schema.additionalItems === "object") children.push(schema.additionalItems);
|
|
468
|
+
if (schema.allOf) children.push(...schema.allOf);
|
|
469
|
+
if (schema.oneOf && schema.oneOf !== schemas) children.push(...schema.oneOf);
|
|
470
|
+
if (schema.anyOf && schema.anyOf !== schemas) children.push(...schema.anyOf);
|
|
471
|
+
if (schema.dependencies) Object.values(schema.dependencies).forEach((dependency) => {
|
|
472
|
+
if (dependency && !Array.isArray(dependency)) children.push(dependency);
|
|
473
|
+
});
|
|
474
|
+
[
|
|
475
|
+
"not",
|
|
476
|
+
"if",
|
|
477
|
+
"then",
|
|
478
|
+
"else"
|
|
479
|
+
].forEach((key) => {
|
|
480
|
+
const child = schema[key];
|
|
481
|
+
if (child && typeof child === "object") children.push(child);
|
|
482
|
+
});
|
|
483
|
+
return children.some(visit);
|
|
484
|
+
};
|
|
485
|
+
return schemas.some(visit);
|
|
486
|
+
}
|
|
487
|
+
getDefinition(ref, options) {
|
|
488
|
+
const [uri, pointer] = ref.split("#/");
|
|
489
|
+
if (uri || !pointer) return null;
|
|
490
|
+
let definition = options.schema;
|
|
491
|
+
for (const path of pointer.split("/")) {
|
|
492
|
+
if (!definition || typeof definition !== "object" || !definition.hasOwnProperty(path)) return null;
|
|
493
|
+
definition = definition[path];
|
|
494
|
+
}
|
|
495
|
+
return typeof definition === "object" ? definition : null;
|
|
496
|
+
}
|
|
497
|
+
resolveDefinition(schema, options) {
|
|
498
|
+
const [uri, pointer] = schema.$ref.split("#/");
|
|
499
|
+
if (uri) throw Error(`Remote schemas for ${schema.$ref} not supported yet.`);
|
|
500
|
+
const definition = !pointer ? null : pointer.split("/").reduce((def, path) => def?.hasOwnProperty(path) ? def[path] : null, options.schema);
|
|
501
|
+
if (!definition) throw Error(`Cannot find a definition for ${schema.$ref}.`);
|
|
502
|
+
if (definition.$ref) return this.resolveDefinition(definition, options);
|
|
503
|
+
return {
|
|
504
|
+
...definition,
|
|
505
|
+
...[
|
|
506
|
+
"title",
|
|
507
|
+
"description",
|
|
508
|
+
"default",
|
|
509
|
+
"widget"
|
|
510
|
+
].reduce((annotation, p) => {
|
|
511
|
+
if (schema.hasOwnProperty(p)) annotation[p] = schema[p];
|
|
512
|
+
return annotation;
|
|
513
|
+
}, {})
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
resolveDependencies(schema) {
|
|
517
|
+
const propDeps = {};
|
|
518
|
+
const schemaDeps = {};
|
|
519
|
+
Object.keys(schema.dependencies || {}).forEach((prop) => {
|
|
520
|
+
const dependency = schema.dependencies[prop];
|
|
521
|
+
if (Array.isArray(dependency)) dependency.forEach((dep) => {
|
|
522
|
+
if (!propDeps[dep]) propDeps[dep] = [prop];
|
|
523
|
+
else propDeps[dep].push(prop);
|
|
524
|
+
});
|
|
525
|
+
else schemaDeps[prop] = dependency;
|
|
526
|
+
});
|
|
527
|
+
return {
|
|
528
|
+
propDeps,
|
|
529
|
+
schemaDeps
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
extractIfCondition(ifSchema) {
|
|
533
|
+
if (ifSchema.properties) {
|
|
534
|
+
const propName = Object.keys(ifSchema.properties)[0];
|
|
535
|
+
if (propName) {
|
|
536
|
+
const propSchema = ifSchema.properties[propName];
|
|
537
|
+
if (propSchema && propSchema.hasOwnProperty("const")) return {
|
|
538
|
+
property: propName,
|
|
539
|
+
value: propSchema.const
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return null;
|
|
544
|
+
}
|
|
545
|
+
resolveIfThenElse(schema, options) {
|
|
546
|
+
const conditionalSchemas = [];
|
|
547
|
+
if (schema.if && typeof schema.if === "object") {
|
|
548
|
+
const condition = this.extractIfCondition(schema.if);
|
|
549
|
+
if (condition) {
|
|
550
|
+
if (schema.then && typeof schema.then === "object") {
|
|
551
|
+
const resolvedSchema = this.resolveConditionalSchema(schema.then, options);
|
|
552
|
+
conditionalSchemas.push({
|
|
553
|
+
...resolvedSchema,
|
|
554
|
+
_ifCondition: condition
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
if (schema.else && typeof schema.else === "object") {
|
|
558
|
+
const resolvedSchema = this.resolveConditionalSchema(schema.else, options);
|
|
559
|
+
conditionalSchemas.push({
|
|
560
|
+
...resolvedSchema,
|
|
561
|
+
_ifCondition: {
|
|
562
|
+
property: condition.property,
|
|
563
|
+
value: condition.value,
|
|
564
|
+
negate: true
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
return conditionalSchemas;
|
|
571
|
+
}
|
|
572
|
+
resolveConditionalSchema(conditionalSchema, options) {
|
|
573
|
+
let resolved = conditionalSchema;
|
|
574
|
+
if (conditionalSchema.$ref) resolved = this.resolveDefinition(conditionalSchema, options);
|
|
575
|
+
if (resolved.allOf) resolved = this.resolveAllOf(resolved, options);
|
|
576
|
+
return resolved;
|
|
577
|
+
}
|
|
578
|
+
guessSchemaType(schema) {
|
|
579
|
+
const type = schema?.type;
|
|
580
|
+
if (!type && schema?.properties) return ["object"];
|
|
581
|
+
if (Array.isArray(type)) {
|
|
582
|
+
if (type.length === 1) return type;
|
|
583
|
+
if (type.length === 2 && type.indexOf("null") !== -1) return type.sort((t1) => t1 == "null" ? 1 : -1);
|
|
584
|
+
return type;
|
|
585
|
+
}
|
|
586
|
+
return type ? [type] : [];
|
|
587
|
+
}
|
|
588
|
+
addValidator(field, name, validator) {
|
|
589
|
+
field.validators = field.validators || {};
|
|
590
|
+
field.validators[name] = validator;
|
|
591
|
+
}
|
|
592
|
+
isEnum(schema) {
|
|
593
|
+
return !!schema.enum || schema.anyOf && schema.anyOf.every(isConst) || schema.oneOf && schema.oneOf.every(isConst) || schema.uniqueItems && schema.items && !Array.isArray(schema.items) && this.isEnum(schema.items);
|
|
594
|
+
}
|
|
595
|
+
toEnumOptions(schema) {
|
|
596
|
+
if (schema.enum) return schema.enum.map((value) => ({
|
|
597
|
+
value,
|
|
598
|
+
label: value
|
|
599
|
+
}));
|
|
600
|
+
const toEnum = (s) => {
|
|
601
|
+
const value = s.hasOwnProperty("const") ? s.const : s.enum[0];
|
|
602
|
+
const option = {
|
|
603
|
+
value,
|
|
604
|
+
label: s.title || value
|
|
605
|
+
};
|
|
606
|
+
if (s.readOnly) option.disabled = true;
|
|
607
|
+
return option;
|
|
608
|
+
};
|
|
609
|
+
if (schema.anyOf) return schema.anyOf.map(toEnum);
|
|
610
|
+
if (schema.oneOf) return schema.oneOf.map(toEnum);
|
|
611
|
+
return this.toEnumOptions(schema.items);
|
|
612
|
+
}
|
|
613
|
+
isFieldValid(root, i, schemas, options, skipMultiSchemaValidation = false) {
|
|
614
|
+
const schema = schemas[i];
|
|
615
|
+
if (!schema._field) Object.defineProperty(schema, "_field", {
|
|
616
|
+
enumerable: false,
|
|
617
|
+
writable: true,
|
|
618
|
+
configurable: true
|
|
619
|
+
});
|
|
620
|
+
const key = ɵhasKey(root) ? root.key : options.key;
|
|
621
|
+
let field = schema._field;
|
|
622
|
+
if (field?.fieldGroup[0].key !== key) field = void 0;
|
|
623
|
+
let model = root.model ? root.model : root.fieldArray ? [] : {};
|
|
624
|
+
if (root.model && ɵhasKey(root)) model = { [Array.isArray(root.key) ? root.key.join(".") : root.key]: ɵgetFieldValue(root) };
|
|
625
|
+
model = ɵclone(model);
|
|
626
|
+
if (!field) field = schema._field = root.options.build({
|
|
627
|
+
form: Array.isArray(model) ? new UntypedFormArray([]) : new UntypedFormGroup({}),
|
|
628
|
+
fieldGroup: [this._toFieldConfig(schema, {
|
|
629
|
+
...options,
|
|
630
|
+
key,
|
|
631
|
+
resetOnHide: true,
|
|
632
|
+
ignoreDefault: true,
|
|
633
|
+
skipMultiSchemaValidation,
|
|
634
|
+
map: null
|
|
635
|
+
})],
|
|
636
|
+
model,
|
|
637
|
+
options: {}
|
|
638
|
+
});
|
|
639
|
+
else {
|
|
640
|
+
field.model = model;
|
|
641
|
+
root.options.build(field);
|
|
642
|
+
}
|
|
643
|
+
return field.form.valid;
|
|
644
|
+
}
|
|
645
|
+
mergeFields(f1, f2) {
|
|
646
|
+
for (const prop in f2) {
|
|
647
|
+
const f1Prop = prop === "templateOptions" ? "props" : prop;
|
|
648
|
+
if (isObject(f1[f1Prop]) && isObject(f2[prop])) f1[f1Prop] = this.mergeFields(f1[f1Prop], f2[prop]);
|
|
649
|
+
else if (f2[prop] != null) f1[f1Prop] = f2[prop];
|
|
650
|
+
}
|
|
651
|
+
return f1;
|
|
652
|
+
}
|
|
653
|
+
static {
|
|
654
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
655
|
+
minVersion: "12.0.0",
|
|
656
|
+
version: "22.2.2",
|
|
657
|
+
ngImport: i0,
|
|
658
|
+
type: FormlyJsonschema,
|
|
659
|
+
deps: [],
|
|
660
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
static {
|
|
664
|
+
this.ɵprov = i0.ɵɵngDeclareInjectable({
|
|
665
|
+
minVersion: "12.0.0",
|
|
666
|
+
version: "22.2.2",
|
|
667
|
+
ngImport: i0,
|
|
668
|
+
type: FormlyJsonschema,
|
|
669
|
+
providedIn: "root"
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
674
|
+
minVersion: "12.0.0",
|
|
675
|
+
version: "22.2.2",
|
|
676
|
+
ngImport: i0,
|
|
677
|
+
type: FormlyJsonschema,
|
|
678
|
+
decorators: [{
|
|
679
|
+
type: Injectable,
|
|
680
|
+
args: [{ providedIn: "root" }]
|
|
681
|
+
}]
|
|
682
|
+
});
|
|
856
683
|
export { FormlyJsonschema };
|
|
857
|
-
|
|
684
|
+
|
|
685
|
+
//# sourceMappingURL=ngx-formly-core-json-schema.mjs.map
|