@ng-formworks/core 17.5.8 → 17.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/esm2022/lib/json-schema-form.component.mjs +24 -6
  2. package/esm2022/lib/json-schema-form.service.mjs +143 -31
  3. package/esm2022/lib/locale/de-validation-messages.mjs +4 -2
  4. package/esm2022/lib/locale/en-validation-messages.mjs +4 -2
  5. package/esm2022/lib/locale/es-validation-messages.mjs +4 -2
  6. package/esm2022/lib/locale/fr-validation-messages.mjs +4 -2
  7. package/esm2022/lib/locale/it-validation-messages.mjs +4 -2
  8. package/esm2022/lib/locale/pt-validation-messages.mjs +4 -2
  9. package/esm2022/lib/locale/zh-validation-messages.mjs +4 -2
  10. package/esm2022/lib/shared/form-group.functions.mjs +326 -14
  11. package/esm2022/lib/shared/format-regex.constants.mjs +2 -1
  12. package/esm2022/lib/shared/index.mjs +6 -6
  13. package/esm2022/lib/shared/json-schema.functions.mjs +96 -4
  14. package/esm2022/lib/shared/json.validators.mjs +11 -6
  15. package/esm2022/lib/shared/jsonpointer.functions.mjs +2 -2
  16. package/esm2022/lib/shared/layout.functions.mjs +172 -14
  17. package/esm2022/lib/shared/utility.functions.mjs +31 -1
  18. package/esm2022/lib/widget-library/add-reference.component.mjs +20 -16
  19. package/esm2022/lib/widget-library/button.component.mjs +5 -2
  20. package/esm2022/lib/widget-library/checkbox.component.mjs +13 -5
  21. package/esm2022/lib/widget-library/checkboxes.component.mjs +10 -3
  22. package/esm2022/lib/widget-library/file.component.mjs +5 -2
  23. package/esm2022/lib/widget-library/hidden.component.mjs +5 -2
  24. package/esm2022/lib/widget-library/input.component.mjs +4 -1
  25. package/esm2022/lib/widget-library/number.component.mjs +4 -1
  26. package/esm2022/lib/widget-library/one-of.component.mjs +88 -4
  27. package/esm2022/lib/widget-library/radios.component.mjs +6 -3
  28. package/esm2022/lib/widget-library/root.component.mjs +74 -1
  29. package/esm2022/lib/widget-library/select.component.mjs +92 -7
  30. package/esm2022/lib/widget-library/submit.component.mjs +3 -2
  31. package/esm2022/lib/widget-library/tabs.component.mjs +77 -29
  32. package/esm2022/lib/widget-library/textarea.component.mjs +5 -2
  33. package/esm2022/lib/widget-library/widget-library.module.mjs +15 -3
  34. package/fesm2022/ng-formworks-core.mjs +1318 -239
  35. package/fesm2022/ng-formworks-core.mjs.map +1 -1
  36. package/lib/json-schema-form.component.d.ts +10 -1
  37. package/lib/json-schema-form.service.d.ts +22 -4
  38. package/lib/shared/form-group.functions.d.ts +29 -1
  39. package/lib/shared/format-regex.constants.d.ts +2 -1
  40. package/lib/shared/index.d.ts +6 -6
  41. package/lib/shared/json-schema.functions.d.ts +22 -0
  42. package/lib/shared/json.validators.d.ts +2 -2
  43. package/lib/shared/layout.functions.d.ts +1 -1
  44. package/lib/shared/utility.functions.d.ts +15 -0
  45. package/lib/shared/validator.functions.d.ts +1 -1
  46. package/lib/widget-library/button.component.d.ts +3 -2
  47. package/lib/widget-library/checkbox.component.d.ts +3 -2
  48. package/lib/widget-library/checkboxes.component.d.ts +3 -2
  49. package/lib/widget-library/file.component.d.ts +3 -2
  50. package/lib/widget-library/hidden.component.d.ts +3 -2
  51. package/lib/widget-library/index.d.ts +1 -1
  52. package/lib/widget-library/input.component.d.ts +3 -2
  53. package/lib/widget-library/number.component.d.ts +3 -2
  54. package/lib/widget-library/one-of.component.d.ts +4 -2
  55. package/lib/widget-library/radios.component.d.ts +3 -2
  56. package/lib/widget-library/root.component.d.ts +1 -0
  57. package/lib/widget-library/select.component.d.ts +5 -2
  58. package/lib/widget-library/textarea.component.d.ts +3 -2
  59. package/package.json +1 -1
@@ -1,16 +1,19 @@
1
1
  import * as i1 from '@angular/common';
2
2
  import { CommonModule } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
- import { Injectable, inject, input, viewChild, ViewContainerRef, Component, Input, Directive, ChangeDetectionStrategy, ViewChild, ElementRef, NgZone, signal, NgModule, Inject, forwardRef, ChangeDetectorRef, output } from '@angular/core';
4
+ import { Injectable, inject, input, viewChild, ViewContainerRef, Component, Input, Directive, ChangeDetectionStrategy, ViewChild, signal, ElementRef, NgZone, NgModule, Inject, forwardRef, ChangeDetectorRef, output } from '@angular/core';
5
5
  import * as i2 from '@angular/forms';
6
6
  import { UntypedFormControl, UntypedFormArray, UntypedFormGroup, FormsModule, ReactiveFormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
7
+ import addFormats from 'ajv-formats';
7
8
  import Ajv2019 from 'ajv/dist/2019';
8
9
  import jsonDraft6 from 'ajv/lib/refs/json-schema-draft-06.json';
9
10
  import jsonDraft7 from 'ajv/lib/refs/json-schema-draft-07.json';
10
11
  import cloneDeep from 'lodash/cloneDeep';
11
12
  import { from, Observable, forkJoin, Subject, BehaviorSubject, lastValueFrom } from 'rxjs';
13
+ import { some, isNil, isEmpty as isEmpty$1, pick, isObject as isObject$1, isEqual as isEqual$2 } from 'lodash';
12
14
  import isEqual$1 from 'lodash/isEqual';
13
15
  import { map, takeUntil } from 'rxjs/operators';
16
+ import omit from 'lodash/omit';
14
17
  import filter from 'lodash/filter';
15
18
  import map$1 from 'lodash/map';
16
19
  import _isArray from 'lodash/isArray';
@@ -61,13 +64,15 @@ const deValidationMessages = {
61
64
  case 'uuid':
62
65
  return 'Keine gültige UUID (z. B. "12345678-9ABC-DEF0-1234-56789ABCDEF0")';
63
66
  case 'color':
64
- return 'Kein gültiger Farbwert (z. B. "#FFFFFF" oder "rgb(255, 255, 255)")';
67
+ return 'Kein gültiger Farbwert (z. B. "#FFFFFF")';
65
68
  case 'json-pointer':
66
69
  return 'Kein gültiger JSON-Pointer (z. B. "/pointer/to/something")';
67
70
  case 'relative-json-pointer':
68
71
  return 'Kein gültiger relativer JSON-Pointer (z. B. "2/pointer/to/something")';
69
72
  case 'regex':
70
73
  return 'Kein gültiger regulärer Ausdruck (z. B. "(1-)?\\d{3}-\\d{3}-\\d{4}")';
74
+ case 'duration':
75
+ return "Muss eine gültige ISO 8601-Dauer sein (z. B. 'PT1H30M')";
71
76
  default:
72
77
  return 'Muss diesem Format entsprechen: ' + error.requiredFormat;
73
78
  }
@@ -121,13 +126,15 @@ const enValidationMessages = {
121
126
  case 'uuid':
122
127
  return 'Must be a uuid, like "12345678-9ABC-DEF0-1234-56789ABCDEF0"';
123
128
  case 'color':
124
- return 'Must be a color, like "#FFFFFF" or "rgb(255, 255, 255)"';
129
+ return 'Must be a color, like "#FFFFFF"';
125
130
  case 'json-pointer':
126
131
  return 'Must be a JSON Pointer, like "/pointer/to/something"';
127
132
  case 'relative-json-pointer':
128
133
  return 'Must be a relative JSON Pointer, like "2/pointer/to/something"';
129
134
  case 'regex':
130
135
  return 'Must be a regular expression, like "(1-)?\\d{3}-\\d{3}-\\d{4}"';
136
+ case 'duration':
137
+ return "Must be a valid ISO 8601 duration (e.g., 'PT1H30M')";
131
138
  default:
132
139
  return 'Must be a correctly formatted ' + error.requiredFormat;
133
140
  }
@@ -179,13 +186,15 @@ const esValidationMessages = {
179
186
  case 'uuid':
180
187
  return 'Debe ser un UUID, ej "12345678-9ABC-DEF0-1234-56789ABCDEF0"';
181
188
  case 'color':
182
- return 'Debe ser un color, ej "#FFFFFF" or "rgb(255, 255, 255)"';
189
+ return 'Debe ser un color, ej "#FFFFFF"';
183
190
  case 'json-pointer':
184
191
  return 'Debe ser un JSON Pointer, ej "/pointer/to/something"';
185
192
  case 'relative-json-pointer':
186
193
  return 'Debe ser un JSON Pointer relativo, ej "2/pointer/to/something"';
187
194
  case 'regex':
188
195
  return 'Debe ser una expresión regular, ej "(1-)?\\d{3}-\\d{3}-\\d{4}"';
196
+ case 'duration':
197
+ return "Debe ser una duración válida en formato ISO 8601 (p. ej., 'PT1H30M')";
189
198
  default:
190
199
  return 'Debe tener el formato correcto ' + error.requiredFormat;
191
200
  }
@@ -238,13 +247,15 @@ const frValidationMessages = {
238
247
  case 'uuid':
239
248
  return 'Doit être un UUID, tel que "12345678-9ABC-DEF0-1234-56789ABCDEF0"';
240
249
  case 'color':
241
- return 'Doit être une couleur, tel que "#FFFFFF" or "rgb(255, 255, 255)"';
250
+ return 'Doit être une couleur, tel que "#FFFFFF"';
242
251
  case 'json-pointer':
243
252
  return 'Doit être un JSON Pointer, tel que "/pointer/to/something"';
244
253
  case 'relative-json-pointer':
245
254
  return 'Doit être un relative JSON Pointer, tel que "2/pointer/to/something"';
246
255
  case 'regex':
247
256
  return 'Doit être une expression régulière, tel que "(1-)?\\d{3}-\\d{3}-\\d{4}"';
257
+ case 'duration':
258
+ return "Doit être une durée valide au format ISO 8601 (par ex., 'PT1H30M')";
248
259
  default:
249
260
  return 'Doit être avoir le format correct: ' + error.requiredFormat;
250
261
  }
@@ -298,13 +309,15 @@ const itValidationMessages = {
298
309
  case 'uuid':
299
310
  return 'Deve essere un uuid, come "12345678-9ABC-DEF0-1234-56789ABCDEF0"';
300
311
  case 'color':
301
- return 'Deve essere un colore, come "#FFFFFF" o "rgb(255, 255, 255)"';
312
+ return 'Deve essere un colore, come "#FFFFFF"';
302
313
  case 'json-pointer':
303
314
  return 'Deve essere un JSON Pointer, come "/pointer/to/something"';
304
315
  case 'relative-json-pointer':
305
316
  return 'Deve essere un JSON Pointer relativo, come "2/pointer/to/something"';
306
317
  case 'regex':
307
318
  return 'Deve essere una regular expression, come "(1-)?\\d{3}-\\d{3}-\\d{4}"';
319
+ case 'duration':
320
+ return "Deve essere una durata valida nel formato ISO 8601 (es. 'PT1H30M')";
308
321
  default:
309
322
  return 'Deve essere formattato correttamente ' + error.requiredFormat;
310
323
  }
@@ -358,13 +371,15 @@ const ptValidationMessages = {
358
371
  case 'uuid':
359
372
  return 'Tem que ser um uuid, por exemplo "12345678-9ABC-DEF0-1234-56789ABCDEF0"';
360
373
  case 'color':
361
- return 'Tem que ser uma cor, por exemplo "#FFFFFF" ou "rgb(255, 255, 255)"';
374
+ return 'Tem que ser uma cor, por exemplo "#FFFFFF"';
362
375
  case 'json-pointer':
363
376
  return 'Tem que ser um JSON Pointer, por exemplo "/referencia/para/algo"';
364
377
  case 'relative-json-pointer':
365
378
  return 'Tem que ser um JSON Pointer relativo, por exemplo "2/referencia/para/algo"';
366
379
  case 'regex':
367
380
  return 'Tem que ser uma expressão regular, por exemplo "(1-)?\\d{3}-\\d{3}-\\d{4}"';
381
+ case 'duration':
382
+ return "Deve ser uma duração válida no formato ISO 8601 (ex.: 'PT1H30M')";
368
383
  default:
369
384
  return 'Tem que ser no formato: ' + error.requiredFormat;
370
385
  }
@@ -418,13 +433,15 @@ const zhValidationMessages = {
418
433
  case 'uuid':
419
434
  return '必须为 uuid, 比如 "12345678-9ABC-DEF0-1234-56789ABCDEF0"';
420
435
  case 'color':
421
- return '必须为颜色值, 比如 "#FFFFFF" 或者 "rgb(255, 255, 255)"';
436
+ return '必须为颜色值, 比如 "#FFFFFF"';
422
437
  case 'json-pointer':
423
438
  return '必须为 JSON Pointer, 比如 "/pointer/to/something"';
424
439
  case 'relative-json-pointer':
425
440
  return '必须为相对的 JSON Pointer, 比如 "2/pointer/to/something"';
426
441
  case 'regex':
427
442
  return '必须为正则表达式, 比如 "(1-)?\\d{3}-\\d{3}-\\d{4}"';
443
+ case 'duration':
444
+ return "必须是有效的 ISO 8601 持续时间(例如:'PT1H30M')";
428
445
  default:
429
446
  return '必须为格式正确的 ' + error.requiredFormat;
430
447
  }
@@ -1411,6 +1428,35 @@ function toTitleCase(input, forceWords) {
1411
1428
  }
1412
1429
  });
1413
1430
  }
1431
+ /**
1432
+ * Recursively checks if at least one property of the given object (including nested objects)
1433
+ * has a non-null and non-undefined value.
1434
+ *
1435
+ * @param obj - The object to check.
1436
+ * @returns `true` if at least one property has a non-null and non-undefined value, otherwise `false`.
1437
+ *
1438
+ * @example
1439
+ * const testObj = { a: null, b: { b1: null, b2: undefined } };
1440
+ * console.log(hasNonNullValue(testObj)); // Output: false
1441
+ *
1442
+ * const testObj2 = { a: 1, b: { b1: null, b2: undefined } };
1443
+ * console.log(hasNonNullValue(testObj2)); // Output: true
1444
+ */
1445
+ function hasNonNullValue(obj) {
1446
+ // If the object is null or not an object, return false immediately
1447
+ if (obj === null || typeof obj !== 'object') {
1448
+ return false;
1449
+ }
1450
+ // _.some checks if at least one element passes the given condition.
1451
+ return some(obj, (value) => {
1452
+ // If value is an object, recurse deeper into the object.
1453
+ if (isObject(value)) {
1454
+ return hasNonNullValue(value);
1455
+ }
1456
+ // Check if value is neither null nor undefined.
1457
+ return !isNil(value);
1458
+ });
1459
+ }
1414
1460
 
1415
1461
  class JsonPointer {
1416
1462
  /**
@@ -2475,6 +2521,7 @@ const jsonSchemaFormatTests = {
2475
2521
  // JSON-pointer: https://tools.ietf.org/html/rfc6901
2476
2522
  'json-pointer': /^(?:\/(?:[^~/]|~0|~1)*)*$|^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i,
2477
2523
  'relative-json-pointer': /^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/,
2524
+ 'duration': /^P(?!$)(\d+Y)?(\d+M)?(\d+D)?(T(\d+H)?(\d+M)?(\d+S)?)?$/,
2478
2525
  'regex': function (str) {
2479
2526
  if (/[^\\]\\Z/.test(str)) {
2480
2527
  return false;
@@ -2771,7 +2818,7 @@ class JsonValidators {
2771
2818
  * This validator currently checks the following formsts:
2772
2819
  * date, time, date-time, email, hostname, ipv4, ipv6,
2773
2820
  * uri, uri-reference, uri-template, url, uuid, color,
2774
- * json-pointer, relative-json-pointer, regex
2821
+ * json-pointer, relative-json-pointer,duration, regex
2775
2822
  *
2776
2823
  * Fast format regular expressions copied from AJV:
2777
2824
  * https://github.com/epoberezkin/ajv/blob/master/lib/compile/formats.js
@@ -2790,9 +2837,14 @@ class JsonValidators {
2790
2837
  let isValid;
2791
2838
  const currentValue = control.value;
2792
2839
  if (isString(currentValue)) {
2840
+ //TODO fix-Reg exp last index problem
2841
+ //see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test
2842
+ //before every call to .test it needs to be reset
2843
+ //use either formatTest.lastIndex = 0; // Reset the lastIndex before each test
2844
+ //or new RegExp(formatTest.source, formatTest.flags);
2793
2845
  const formatTest = jsonSchemaFormatTests[requiredFormat];
2794
2846
  if (typeof formatTest === 'object') {
2795
- isValid = formatTest.test(currentValue);
2847
+ isValid = new RegExp(formatTest.source, formatTest.flags).test(currentValue);
2796
2848
  }
2797
2849
  else if (typeof formatTest === 'function') {
2798
2850
  isValid = formatTest(currentValue);
@@ -4029,8 +4081,18 @@ function getInputType(schema, layoutNode = null) {
4029
4081
  if (hasOwn(schema, '$ref')) {
4030
4082
  return '$ref';
4031
4083
  }
4032
- if (isArray(schema.oneOf) || isArray(schema.anyOf)) {
4084
+ //if (isArray(schema.anyOf)) { return 'any-of'; }//treated as allOf
4085
+ if (isArray(schema.oneOf)) {
4033
4086
  return 'one-of';
4087
+ } //{ return 'tabarray'; }
4088
+ if (hasOwn(schema, 'if')) {
4089
+ return 'if';
4090
+ }
4091
+ if (hasOwn(schema, 'then')) {
4092
+ return 'then';
4093
+ }
4094
+ if (hasOwn(schema, 'else')) {
4095
+ return 'else';
4034
4096
  }
4035
4097
  console.error(`getInputType error: Unable to determine input type for ${schemaType}`);
4036
4098
  console.error('schema', schema);
@@ -4443,6 +4505,14 @@ function getSubSchema(schema, pointer, schemaRefLibrary = null, schemaRecursiveR
4443
4505
  return subSchema;
4444
4506
  }, true, pointer);
4445
4507
  }
4508
+ function omitKeys(objects, keysToOmit) {
4509
+ return objects.map((obj) => omit(obj, keysToOmit));
4510
+ }
4511
+ function combineAllOfITE(schema) {
4512
+ if (schema && schema.allOf) {
4513
+ const allITE = schema.allOf.map(item => { return item.if && { if: item.if }; });
4514
+ }
4515
+ }
4446
4516
  /**
4447
4517
  * 'combineAllOf' function
4448
4518
  *
@@ -4456,11 +4526,24 @@ function combineAllOf(schema) {
4456
4526
  if (!isObject(schema) || !isArray(schema.allOf)) {
4457
4527
  return schema;
4458
4528
  }
4459
- let mergedSchema = mergeSchemas(...schema.allOf);
4529
+ const allITE = schema.allOf.map(item => { return item.if && item; })
4530
+ .filter(item => !isEmpty$1(item));
4531
+ //adaped to accomodate ITE by merging all non ITE field
4532
+ //then readding the allOf key with only ITE
4533
+ let schemaITEOmitted = omitKeys(schema.allOf, ['if', 'then', 'else']);
4534
+ let mergedSchema = mergeSchemas(...schemaITEOmitted);
4535
+ //mergeSchemas(...schema.allOf);
4460
4536
  if (Object.keys(schema).length > 1) {
4461
4537
  const extraKeys = { ...schema };
4462
4538
  delete extraKeys.allOf;
4463
- mergedSchema = mergeSchemas(mergedSchema, extraKeys);
4539
+ //TODO Test-changed order to preserve originial order
4540
+ mergedSchema = mergeSchemas(extraKeys, mergedSchema);
4541
+ //mergeSchemas(mergedSchema, extraKeys);
4542
+ //need to put it back if ITE
4543
+ if (allITE && allITE.length > 0) {
4544
+ mergedSchema.allOf = mergedSchema.allOf || [];
4545
+ mergedSchema.allOf.push(...allITE);
4546
+ }
4464
4547
  }
4465
4548
  return mergedSchema;
4466
4549
  }
@@ -4486,6 +4569,65 @@ function fixRequiredArrayProperties(schema) {
4486
4569
  }
4487
4570
  return schema;
4488
4571
  }
4572
+ /**
4573
+ * 'convertJSONSchemaIfToCondition' function
4574
+ * converts something like
4575
+ * "if": {
4576
+ * "properties": {
4577
+ * "animal": {
4578
+ * "const": "Cat"
4579
+ * },
4580
+ * "habitat":{
4581
+ * "const": "City"
4582
+ * }
4583
+ * }
4584
+ * }
4585
+ * to "model.animal=='Cat' && habitat=='City" contion
4586
+ * @param schema:any
4587
+ * @param negate:boolean=false
4588
+ * @returns
4589
+
4590
+ */
4591
+ function convertJSONSchemaIfToCondition(schema, layoutNode, negate = false) {
4592
+ let conditionFun = "";
4593
+ let condition = {};
4594
+ let notOp = negate ? "!" : "";
4595
+ // expects "dataPointer" to be like "/a/b/c"
4596
+ //TODO-test
4597
+ //dataPointer can be something like /cities/-/name
4598
+ //must end up like model.cities[arrayIndices].name
4599
+ //also check can possibly be nested array like /cities/-/sites/-/siteName
4600
+ //in this case must probably end up like
4601
+ // /cities/arrayIndices[0]/sites/arrayIndices[1]/siteName
4602
+ //but it seems evaluatCondition support only one level for now
4603
+ //and uses arrayIndices as the last index only -check?
4604
+ let parentPath = layoutNode.dataPointer ? layoutNode.dataPointer
4605
+ .split("/")
4606
+ .slice(1, -1)
4607
+ .map((part, ind) => {
4608
+ let sep = ind == 0 ? "" : ".";
4609
+ let ret = part == "-" ? "[arrayIndices]" : sep + part;
4610
+ return ret;
4611
+ })
4612
+ .join("")
4613
+ : "";
4614
+ let modelPath = parentPath ? `model.${parentPath}` : "model";
4615
+ let checkPath = modelPath.split(".")
4616
+ .reduce((accumulator, currentPart, index) => {
4617
+ const currentExpression = index === 0 ? currentPart : `${accumulator}.${currentPart}`;
4618
+ return index === 0 ? currentExpression : `${accumulator} && ${currentExpression}`;
4619
+ }, '');
4620
+ if (schema.if) {
4621
+ Object.keys(schema.if.properties).forEach((ifProp, ind) => {
4622
+ let amper = ind > 0 ? "&" : "";
4623
+ //Note the model value is first converted to string and so is the condition
4624
+ //so that booleans and numbers can also be compared
4625
+ conditionFun += `${amper} ${checkPath} && ${modelPath}.${ifProp}+""=='${schema.if.properties[ifProp].const}'`;
4626
+ });
4627
+ }
4628
+ condition["functionBody"] = `return ${notOp}(${conditionFun})`;
4629
+ return condition;
4630
+ }
4489
4631
 
4490
4632
  function convertSchemaToDraft6(schema, options = {}) {
4491
4633
  let draft = options.draft || null;
@@ -4786,6 +4928,17 @@ function convertSchemaToDraft6(schema, options = {}) {
4786
4928
  return newSchema;
4787
4929
  }
4788
4930
 
4931
+ /**
4932
+ * path2ControlKey takes a datapointer path like /some/pointer/path
4933
+ * and returns something like $some$pointer$path
4934
+ * used mainly to convert paths so it can be used as keys in FormGroups
4935
+ * fot ITE scenarios
4936
+ * @param path
4937
+ * @returns string
4938
+ */
4939
+ function path2ControlKey(path) {
4940
+ return path.replace(/\//g, "$");
4941
+ }
4789
4942
  /**
4790
4943
  * FormGroup function library:
4791
4944
  *
@@ -4834,11 +4987,13 @@ function buildFormGroupTemplate(jsf, nodeValue = null, setValues = true, schemaP
4834
4987
  }
4835
4988
  // TODO: If nodeValue still not set, check layout for default value
4836
4989
  const schemaType = JsonPointer.get(schema, '/type');
4837
- const controlType = (hasOwn(schema, 'properties') || hasOwn(schema, 'additionalProperties')) &&
4838
- schemaType === 'object' ? 'FormGroup' :
4839
- (hasOwn(schema, 'items') || hasOwn(schema, 'additionalItems')) &&
4840
- schemaType === 'array' ? 'FormArray' :
4841
- !schemaType && hasOwn(schema, '$ref') ? '$ref' : 'FormControl';
4990
+ const isIfThenElse = hasOwn(schema, 'if') || hasOwn(schema, 'then') || hasOwn(schema, 'else');
4991
+ const controlType = isIfThenElse && !schemaType ? 'IfThenElse' :
4992
+ (hasOwn(schema, 'properties') || hasOwn(schema, 'additionalProperties')) &&
4993
+ schemaType === 'object' ? 'FormGroup' :
4994
+ (hasOwn(schema, 'items') || hasOwn(schema, 'additionalItems')) &&
4995
+ schemaType === 'array' ? 'FormArray' :
4996
+ !schemaType && hasOwn(schema, '$ref') ? '$ref' : 'FormControl';
4842
4997
  const shortDataPointer = removeRecursiveReferences(dataPointer, jsf.dataRecursiveRefMap, jsf.arrayMap);
4843
4998
  if (!jsf.dataMap.has(shortDataPointer)) {
4844
4999
  jsf.dataMap.set(shortDataPointer, new Map());
@@ -4877,11 +5032,175 @@ function buildFormGroupTemplate(jsf, nodeValue = null, setValues = true, schemaP
4877
5032
  propertyKeys
4878
5033
  .filter(key => hasOwn(schema.properties, key) ||
4879
5034
  hasOwn(schema, 'additionalProperties'))
4880
- .forEach(key => controls[key] = buildFormGroupTemplate(jsf, JsonPointer.get(nodeValue, [key]), setValues, schemaPointer + (hasOwn(schema.properties, key) ?
4881
- '/properties/' + key : '/additionalProperties'), dataPointer + '/' + key, templatePointer + '/controls/' + key));
5035
+ .forEach(key => {
5036
+ controls[key] = buildFormGroupTemplate(jsf, JsonPointer.get(nodeValue, [key]), setValues, schemaPointer + (hasOwn(schema.properties, key) ?
5037
+ '/properties/' + key : '/additionalProperties'), dataPointer + '/' + key, templatePointer + '/controls/' + key);
5038
+ //add the $<control> type to the root
5039
+ //so it can be flattened and acceses directly in the formgroup
5040
+ //by its full '$' path
5041
+ ["allOf", "anyOf", "oneOf"].forEach(ofType => {
5042
+ if (controls[key].controls && controls[key].controls[`_${ofType}`]) {
5043
+ Object.keys(controls[key].controls[`_${ofType}`]).forEach($key => {
5044
+ controls[$key] = controls[key].controls[`_${ofType}`][$key];
5045
+ delete controls[key].controls[$key];
5046
+ });
5047
+ delete controls[key].controls[`_${ofType}`];
5048
+ }
5049
+ });
5050
+ });
5051
+ if (hasOwn(schema, "if")) {
5052
+ ["then", "else"].forEach(con => {
5053
+ if (hasOwn(schema, con)) {
5054
+ const keySchemaPointer = `/${con}`;
5055
+ let thenFGTemplate = buildFormGroupTemplate(jsf, nodeValue, false, //JsonPointer.get(nodeValue, keySchemaPointer), setValues,
5056
+ schemaPointer + keySchemaPointer, dataPointer, templatePointer + `/controls/${con}`);
5057
+ Object.assign(controls, thenFGTemplate.controls);
5058
+ }
5059
+ });
5060
+ }
5061
+ /* treat allOf the same as any of but need to add an extra
5062
+ condition for which anyOf item is to be rendered
5063
+ let allOfControls = {}
5064
+ let allOfAllowedKeys = ["allOf", "anyOf", "oneOf", "if", "then", "else", "type", "properties", "items"];
5065
+ if (hasOwn(schema, "allOf") && isArray(schema.allOf)) {
5066
+ schema.allOf.forEach((allOfItem, ind) => {
5067
+ let aoItemKeys = Object.keys(allOfItem);
5068
+ let foundKeys = allOfAllowedKeys.filter(value =>
5069
+ aoItemKeys.includes(value)
5070
+ );
5071
+ if (foundKeys && foundKeys.length > 0) {
5072
+ const keySchemaPointer = `/allOf/${ind}`;
5073
+ //console.log(`found:${keySchemaPointer}`);
5074
+ let allOfFGTemplate = buildFormGroupTemplate(
5075
+ jsf, JsonPointer.get(nodeValue, keySchemaPointer), setValues,
5076
+ schemaPointer + keySchemaPointer,
5077
+ dataPointer,
5078
+ templatePointer + '/controls/' + ind
5079
+ );
5080
+ if (allOfFGTemplate.controls) {
5081
+ Object.keys(allOfFGTemplate.controls).forEach(key => {
5082
+ let controlKey = allOfFGTemplate.controls[key].schemaPointer || `${schemaPointer}${keySchemaPointer}/${key}`;
5083
+ controlKey = path2ControlKey(controlKey);
5084
+ controls[controlKey] = {
5085
+ key: key,
5086
+ schemaPointer: schemaPointer + keySchemaPointer,
5087
+ controls: allOfFGTemplate.controls[key]
5088
+ }
5089
+ controls[controlKey] = allOfFGTemplate.controls[key];
5090
+ controls[controlKey].key = key;
5091
+ controls[controlKey].schemaPointer = allOfFGTemplate.controls[key].schemaPointer || schemaPointer + keySchemaPointer;
5092
+
5093
+ controls[key] = allOfFGTemplate.controls[key];
5094
+ })
5095
+ }
5096
+ //add ui type items to controls
5097
+ if (allOfItem["type"] || allOfItem["properties"] || allOfItem["items"]) {
5098
+ allOfControls[ind] = allOfFGTemplate
5099
+ }
5100
+
5101
+ }
5102
+
5103
+ })
5104
+ controls["allOf"] = allOfControls;
5105
+ }
5106
+ */
5107
+ let ofAllowedKeys = ["allOf", "anyOf", "oneOf", "if", "then", "else", "type", "properties", "items"];
5108
+ ["allOf", "anyOf", "oneOf"].forEach(ofType => {
5109
+ if (hasOwn(schema, ofType) && isArray(schema[ofType])) {
5110
+ schema[ofType].forEach((ofItem, ind) => {
5111
+ let aoItemKeys = Object.keys(ofItem);
5112
+ let foundKeys = ofAllowedKeys.filter(value => aoItemKeys.includes(value));
5113
+ if (foundKeys && foundKeys.length > 0) {
5114
+ const keySchemaPointer = `/${ofType}/${ind}`;
5115
+ //console.log(`found:${keySchemaPointer}`);
5116
+ let newNodeValue = JsonPointer.get(nodeValue, dataPointer);
5117
+ //JsonPointer.get(nodeValue, keySchemaPointer);
5118
+ if (ofType == "oneOf") {
5119
+ newNodeValue = nodeValue;
5120
+ }
5121
+ let allOfFGTemplate = buildFormGroupTemplate(jsf, newNodeValue, setValues, schemaPointer + keySchemaPointer, dataPointer, templatePointer + '/controls/' + ind);
5122
+ if (allOfFGTemplate.controls) {
5123
+ Object.keys(allOfFGTemplate.controls).forEach(key => {
5124
+ const l2SchemaPointer = hasOwn(schema, 'properties') ?
5125
+ '/properties/' + key : key;
5126
+ let controlKey = allOfFGTemplate.controls[key].schemaPointer || `${schemaPointer}${keySchemaPointer}${l2SchemaPointer}`;
5127
+ controlKey = path2ControlKey(controlKey);
5128
+ /*
5129
+ controls[controlKey] = {
5130
+ key: key,
5131
+ schemaPointer: `${schemaPointer}${keySchemaPointer}/${key}`,//schemaPointer + keySchemaPointer,
5132
+ controls: allOfFGTemplate.controls[key]
5133
+ }
5134
+ */
5135
+ let controlItem = cloneDeep(allOfFGTemplate.controls[key]);
5136
+ controlItem.key = key;
5137
+ controlItem.schemaPointer = controlItem.schemaPointer || `${schemaPointer}${keySchemaPointer}${l2SchemaPointer}`;
5138
+ controls[controlKey] = controlItem;
5139
+ //need to test if value matches schema,
5140
+ //as the same oneOf item will be assigned to the same value
5141
+ //if key is a $oneOf key then it was inserted at the root of the controls
5142
+ //as form control name will be the full(escaped) path
5143
+ const pointerPath = key.startsWith('$oneOf') ? controlItem.schemaPointer : keySchemaPointer;
5144
+ let oneOfItemSchema = JsonPointer.get(jsf.schema, controlItem.schemaPointer);
5145
+ //JsonPointer.get(schema,pointerPath);
5146
+ let dPointer = controlItem.schemaPointer.replace(/(anyOf|allOf|oneOf|none)\/[\d]+\//g, '')
5147
+ .replace(/(if|then|else|properties)\//g, '');
5148
+ //JsonPointer.toDataPointer(controlItem.schemaPointer,jsf.schema);
5149
+ let dVal = JsonPointer.get(nodeValue, dPointer);
5150
+ let fkey = key;
5151
+ let oneOfItemValue = dVal;
5152
+ /*
5153
+ if(hasOwn(oneOfItemSchema,"if") && controlItem.schemaPointer
5154
+ && controlItem.schemaPointer.indexOf(keySchemaPointer)==0){
5155
+ let parts=controlItem.schemaPointer
5156
+ .split(keySchemaPointer).join('').split("/")
5157
+ let thenOrElse=parts[1];
5158
+ fkey=parts[parts.length-1];
5159
+ oneOfItemSchema=oneOfItemSchema[thenOrElse];
5160
+ }
5161
+
5162
+ if(oneOfItemSchema.properties && jsf.formValues===undefined){
5163
+ //check if no form data values were supplied
5164
+ //then set it to default otherwise to its nodevalue
5165
+ oneOfItemValue=oneOfItemSchema.default
5166
+ oneOfItemValue[fkey]=oneOfItemSchema.properties[fkey]?.default;
5167
+ }
5168
+ if(oneOfItemSchema.properties && jsf.formValues!=undefined){
5169
+ oneOfItemValue ={};
5170
+ //nodeValue||{};
5171
+ oneOfItemValue[fkey]=nodeValue&&nodeValue[fkey];
5172
+ }
5173
+ if(!oneOfItemSchema.properties && jsf.formValues==undefined){
5174
+ oneOfItemValue=oneOfItemSchema.default;
5175
+ }
5176
+ */
5177
+ if (hasOwn(controlItem, "value")) {
5178
+ if (!jsf.ajv.validate(oneOfItemSchema, oneOfItemValue)) {
5179
+ controlItem.value.value = null;
5180
+ }
5181
+ else {
5182
+ ///controlItem.value.value=oneOfItemValue[fkey];
5183
+ controlItem.value.value = oneOfItemSchema.properties ? oneOfItemValue[fkey] : oneOfItemValue;
5184
+ }
5185
+ }
5186
+ //controls[controlKey] = controlItem;
5187
+ //allOfFGTemplate.controls[key].schemaPointer ||`${schemaPointer}${keySchemaPointer}/${key}`;
5188
+ //allOfFGTemplate.controls[key].schemaPointer || schemaPointer + keySchemaPointer;
5189
+ ///////controls[key] = cloneDeep(allOfFGTemplate.controls[key]);
5190
+ //add schemacontrol to root
5191
+ //controls[controlKey]=controlItem
5192
+ controls[`_${ofType}`] = controls[`_${ofType}`] || {};
5193
+ controls[`_${ofType}`][controlKey] = controlItem;
5194
+ //allOfFGTemplate.controls[key];
5195
+ });
5196
+ }
5197
+ }
5198
+ });
5199
+ }
5200
+ });
4882
5201
  jsf.formOptions.fieldsRequired = setRequiredFields(schema, controls);
4883
5202
  }
4884
- return { controlType, controls, validators };
5203
+ return { controlType, controls, validators, schemaPointer };
4885
5204
  case 'FormArray':
4886
5205
  controls = [];
4887
5206
  const minItems = Math.max(schema.minItems || 0, nodeOptions.get('minItems') || 0);
@@ -4938,7 +5257,7 @@ function buildFormGroupTemplate(jsf, nodeValue = null, setValues = true, schemaP
4938
5257
  }
4939
5258
  }
4940
5259
  }
4941
- return { controlType, controls, validators };
5260
+ return { controlType, controls, validators, schemaPointer };
4942
5261
  case '$ref':
4943
5262
  const schemaRef = JsonPointer.compile(schema.$ref);
4944
5263
  const dataRef = JsonPointer.toDataPointer(schemaRef, schema);
@@ -4960,7 +5279,57 @@ function buildFormGroupTemplate(jsf, nodeValue = null, setValues = true, schemaP
4960
5279
  value: setValues && isPrimitive(nodeValue) ? nodeValue : null,
4961
5280
  disabled: nodeOptions.get('disabled') || false
4962
5281
  };
4963
- return { controlType, value, validators };
5282
+ return { controlType, value, validators, schemaPointer };
5283
+ //TODO may make an IFThenElse widget or integrate it with the section
5284
+ //widget
5285
+ case 'IfThenElse':
5286
+ controls = {};
5287
+ let conditionType;
5288
+ if (hasOwn(schema, "if")) {
5289
+ ["then", "else"].forEach(con => {
5290
+ if (hasOwn(schema, con)) {
5291
+ const keySchemaPointer = `/${con}`;
5292
+ let thenTFGTemplate = buildFormGroupTemplate(jsf, nodeValue, false, schemaPointer + keySchemaPointer, dataPointer, templatePointer + `/controls/${con}`);
5293
+ //NB same property can be in both then and else
5294
+ //so key must be the unique path to control
5295
+ //let ifItemSchema=JsonPointer.get(schema,keySchemaPointer);
5296
+ //let ifItemValue;
5297
+ Object.keys(thenTFGTemplate.controls).forEach(key => {
5298
+ let controlKey = thenTFGTemplate.controls[key].schemaPointer;
5299
+ ////let controlItem=cloneDeep(thenTFGTemplate.controls[key]);
5300
+ ////thenTFGTemplate.controls[key].schemaPointer || `${schemaPointer}${keySchemaPointer}/${key}`;
5301
+ controlKey = path2ControlKey(controlKey);
5302
+ let cItem = Object.assign({}, thenTFGTemplate.controls[key]);
5303
+ ////cItem.schemaPointer = `${schemaPointer}${keySchemaPointer}/${key}`;
5304
+ /*
5305
+ if(ifItemSchema.properties && jsf.formValues===undefined){
5306
+ //check if no form data values were supplied
5307
+ //then set it to default otherwise to its nodevalue
5308
+ ifItemValue=ifItemSchema.default
5309
+ ifItemValue[key]=ifItemSchema.properties[key]?.default;
5310
+ }
5311
+ if(ifItemSchema.properties && jsf.formValues!=undefined){
5312
+ ifItemValue ={};
5313
+ //nodeValue||{};
5314
+ ifItemValue[key]=nodeValue&&nodeValue[key];
5315
+ }
5316
+ if(!ifItemSchema.properties && jsf.formValues==undefined){
5317
+ ifItemValue=ifItemSchema.default;
5318
+ }
5319
+ if(hasOwn(cItem,"value")){
5320
+ if(!jsf.ajv.validate(ifItemSchema,ifItemValue)){
5321
+ cItem.value.value=null;
5322
+ }else{
5323
+ cItem.value.value=ifItemValue[key];
5324
+ }
5325
+ }
5326
+ */
5327
+ controls[controlKey] = cItem;
5328
+ });
5329
+ }
5330
+ });
5331
+ }
5332
+ return { controlType, controls, validators, schemaPointer };
4964
5333
  default:
4965
5334
  return null;
4966
5335
  }
@@ -4992,7 +5361,23 @@ function buildFormGroup(template) {
4992
5361
  const groupControls = {};
4993
5362
  forEach(template.controls, (controls, key) => {
4994
5363
  const newControl = buildFormGroup(controls);
5364
+ //if (newControl) { groupControls[key] = newControl; }
4995
5365
  if (newControl) {
5366
+ /* experimental idea was to try to be able to switch
5367
+ conditional controls dynamically based on their schema pointer
5368
+ (not datapointer as that only maps to one control)
5369
+ Object.defineProperty(groupControls, key, {
5370
+ get: () => {
5371
+ //console.log(`Accessed control: ${key}`);
5372
+ //add switch logic here
5373
+ return ncontrol;
5374
+ },
5375
+ set:(value)=>{
5376
+ ncontrol=value
5377
+ },
5378
+ enumerable: true
5379
+ })
5380
+ */
4996
5381
  groupControls[key] = newControl;
4997
5382
  }
4998
5383
  });
@@ -5142,6 +5527,9 @@ function formatFormData(formData, dataMap, recursiveRefMap, arrayMap, returnEmpt
5142
5527
  }
5143
5528
  else if (typeof value !== 'object' || isDate(value) ||
5144
5529
  (value === null && returnEmptyFields)) {
5530
+ if (genericPointer.indexOf("/$") >= 0) {
5531
+ return formattedData;
5532
+ }
5145
5533
  console.error('formatFormData error: ' +
5146
5534
  `Schema type not found for form value at ${genericPointer}`);
5147
5535
  console.error('dataMap', dataMap);
@@ -5166,14 +5554,15 @@ function formatFormData(formData, dataMap, recursiveRefMap, arrayMap, returnEmpt
5166
5554
  * // {Pointer} dataPointer - JSON Pointer (string or array)
5167
5555
  * // {boolean = false} returnGroup - If true, return group containing control
5168
5556
  * // {group} - Located value (or null, if no control found)
5557
+ * // {string} schemaPointer - string used for conditional controls coming from schema if/then/else
5169
5558
  */
5170
- function getControl(formGroup, dataPointer, returnGroup = false) {
5559
+ function getControl(formGroup, dataPointer, returnGroup = false, schemaPointer) {
5171
5560
  if (!isObject(formGroup) || !JsonPointer.isJsonPointer(dataPointer)) {
5172
5561
  if (!JsonPointer.isJsonPointer(dataPointer)) {
5173
5562
  // If dataPointer input is not a valid JSON pointer, check to
5174
5563
  // see if it is instead a valid object path, using dot notaion
5175
5564
  if (typeof dataPointer === 'string') {
5176
- const formControl = formGroup.get(dataPointer);
5565
+ const formControl = formGroup.get(path2ControlKey(schemaPointer || "")) || formGroup.get(dataPointer);
5177
5566
  if (formControl) {
5178
5567
  return formControl;
5179
5568
  }
@@ -5193,7 +5582,7 @@ function getControl(formGroup, dataPointer, returnGroup = false) {
5193
5582
  // try using formGroup.get() to return the control
5194
5583
  if (typeof formGroup.get === 'function' &&
5195
5584
  dataPointerArray.every(key => key.indexOf('.') === -1)) {
5196
- const formControl = formGroup.get(dataPointerArray.join('.'));
5585
+ const formControl = formGroup.get(path2ControlKey(schemaPointer || "")) || formGroup.get(dataPointerArray.join('.'));
5197
5586
  if (formControl) {
5198
5587
  return formControl;
5199
5588
  }
@@ -5212,6 +5601,9 @@ function getControl(formGroup, dataPointer, returnGroup = false) {
5212
5601
  else if (hasOwn(subGroup, key)) {
5213
5602
  subGroup = subGroup[key];
5214
5603
  }
5604
+ else if (schemaPointer && hasOwn(subGroup, path2ControlKey(schemaPointer))) {
5605
+ subGroup = subGroup[path2ControlKey(schemaPointer)];
5606
+ }
5215
5607
  else {
5216
5608
  console.error(`getControl error: Unable to find "${key}" item in FormGroup.`);
5217
5609
  console.error(dataPointer);
@@ -5221,6 +5613,68 @@ function getControl(formGroup, dataPointer, returnGroup = false) {
5221
5613
  }
5222
5614
  return subGroup;
5223
5615
  }
5616
+ /**
5617
+ * 'setControl' function
5618
+ *
5619
+ * Uses a JSON Pointer for a data object to retrieve a control from
5620
+ * an Angular formGroup or formGroup template. (Note: though a formGroup
5621
+ * template is much simpler, its basic structure is idential to a formGroup).
5622
+ *
5623
+ * If the optional third parameter 'returnGroup' is set to TRUE, the group
5624
+ * containing the control is returned, rather than the control itself.
5625
+ *
5626
+ * // {FormGroup} formGroup - Angular FormGroup to get value from
5627
+ * // {Pointer} dataPointer - JSON Pointer (string or array)
5628
+ * // {AbstractControl} control - control used to replace existing or add
5629
+ * // {targetKey} - optional string used as the new key-not implemented as yet
5630
+ */
5631
+ function setControl(formGroup, dataPointer, control, targetKey) {
5632
+ let dataPointerArray = JsonPointer.parse(dataPointer);
5633
+ // If formGroup input is a real formGroup (not a formGroup template)
5634
+ // try using formGroup.get() to return the control
5635
+ /*
5636
+ if (typeof formGroup.get === 'function' &&
5637
+ dataPointerArray.every(key => key.indexOf('.') === -1)
5638
+ ) {
5639
+ formGroup.setControl(dataPointerArray.join('.'), control);
5640
+ return;
5641
+ }
5642
+ */
5643
+ let currentGroup = formGroup;
5644
+ for (let i = 0; i < dataPointerArray.length - 1; i++) {
5645
+ // Navigate down the form structure to find the correct nested FormGroup
5646
+ currentGroup = currentGroup.get(dataPointerArray[i]);
5647
+ // If it's not a FormGroup, we throw an error since we can't set a control in a non-group.
5648
+ if (!(typeof currentGroup.setControl === 'function')) {
5649
+ throw new Error(`Path '${dataPointerArray[i]}' is not a valid FormGroup or FormArray.`);
5650
+ }
5651
+ }
5652
+ // Now we are at the parent FormGroup, set the control at the last part of the path
5653
+ const lastPart = dataPointerArray[dataPointerArray.length - 1];
5654
+ // Set the control at the final path (like 'name' inside 'state')
5655
+ currentGroup.setControl(lastPart, control);
5656
+ // If formGroup input is a formGroup template,
5657
+ // or formGroup.get() failed to return the control,
5658
+ // search the formGroup object for dataPointer's control
5659
+ //TODO needs to be adapted to setControl
5660
+ /*
5661
+ let subGroup = formGroup;
5662
+ for (const key of dataPointerArray) {
5663
+ if (hasOwn(subGroup, 'controls')) { subGroup = subGroup.controls; }
5664
+ if (isArray(subGroup) && (key === '-')) {
5665
+ subGroup = subGroup[subGroup.length - 1];
5666
+ } else if (hasOwn(subGroup, key)) {
5667
+ subGroup = subGroup[key];
5668
+ } else {
5669
+ console.error(`getControl error: Unable to find "${key}" item in FormGroup.`);
5670
+ console.error(dataPointer);
5671
+ console.error(formGroup);
5672
+ return;
5673
+ }
5674
+ }
5675
+ return subGroup;
5676
+ */
5677
+ }
5224
5678
 
5225
5679
  /**
5226
5680
  * Layout function library:
@@ -5390,7 +5844,6 @@ function buildLayout_original(jsf, widgetLibrary) {
5390
5844
  schemaPointer = JsonPointer.toSchemaPointer(shortDataPointer, jsf.schema);
5391
5845
  nodeDataMap.set('schemaPointer', schemaPointer);
5392
5846
  }
5393
- nodeDataMap.set('disabled', !!newNode.options.disabled);
5394
5847
  nodeSchema = JsonPointer.get(jsf.schema, schemaPointer);
5395
5848
  if (nodeSchema) {
5396
5849
  if (!hasOwn(newNode, 'type')) {
@@ -5411,6 +5864,7 @@ function buildLayout_original(jsf, widgetLibrary) {
5411
5864
  newNode.dataType =
5412
5865
  nodeSchema.type || (hasOwn(nodeSchema, '$ref') ? '$ref' : null);
5413
5866
  updateInputOptions(newNode, nodeSchema, jsf);
5867
+ nodeDataMap.set('disabled', !!newNode.options.disabled);
5414
5868
  // Present checkboxes as single control, rather than array
5415
5869
  if (newNode.type === 'checkboxes' && hasOwn(nodeSchema, 'items')) {
5416
5870
  updateInputOptions(newNode, nodeSchema.items, jsf);
@@ -5833,16 +6287,50 @@ function fixNestedArrayLayout(options) {
5833
6287
  * // { string = '' } dataPointerPrefix -
5834
6288
  * //
5835
6289
  */
5836
- function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPointer = '', dataPointer = '', arrayItem = false, arrayItemType = null, removable = null, forRefLibrary = false, dataPointerPrefix = '') {
5837
- const schema = JsonPointer.get(jsf.schema, schemaPointer);
6290
+ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPointer = '', dataPointer = '', arrayItem = false, arrayItemType = null, removable = null, forRefLibrary = false, dataPointerPrefix = '', jsonSchema) {
6291
+ function applyITEConditions(builtLayout, schPointer, keySchemaPointer, negateClause, parentLayout) {
6292
+ if (builtLayout) {
6293
+ if (parentLayout && parentLayout.isITEItem && parentLayout.options.condition) {
6294
+ return;
6295
+ }
6296
+ if (isArray(builtLayout)) {
6297
+ builtLayout.forEach(item => {
6298
+ item.isITEItem = true;
6299
+ item.options.condition = convertJSONSchemaIfToCondition(schema, item, negateClause);
6300
+ applyITEConditions(item, schPointer, keySchemaPointer, negateClause, builtLayout);
6301
+ //item.schemaPointer = schPointer + keySchemaPointer + item.dataPointer;
6302
+ //item.options.condition = convertJSONSchemaIfToCondition(schema, negateClause);
6303
+ //newSection.push(item);
6304
+ });
6305
+ }
6306
+ else if (hasOwn(builtLayout, "items")) {
6307
+ applyITEConditions(builtLayout.items, schPointer, keySchemaPointer, negateClause, builtLayout);
6308
+ // builtLayout.items.forEach(item => {
6309
+ // item.isITEItem=true;
6310
+ // item.options.condition = convertJSONSchemaIfToCondition(schema,item, negateClause);
6311
+ // applyITEConditions(item,schPointer,keySchemaPointer,negateClause)
6312
+ // });
6313
+ }
6314
+ else {
6315
+ builtLayout.isITEItem = true;
6316
+ //builtLayout.schemaPointer = `${schPointer}${keySchemaPointer}/${builtLayout.name}`;
6317
+ builtLayout.options.condition = convertJSONSchemaIfToCondition(schema, builtLayout, negateClause);
6318
+ //newSection.push(builtLayout)
6319
+ }
6320
+ }
6321
+ }
6322
+ const jsSchema = jsonSchema || jsf.schema;
6323
+ const schema = JsonPointer.get(jsSchema, schemaPointer);
6324
+ //JsonPointer.get(jsf.schema, schemaPointer);
5838
6325
  if (!hasOwn(schema, 'type') && !hasOwn(schema, '$ref') &&
5839
- !hasOwn(schema, 'x-schema-form')) {
6326
+ !hasOwn(schema, 'x-schema-form')
6327
+ && !hasOwn(schema, 'if') && !hasOwn(schema, 'then') && !hasOwn(schema, 'else')) {
5840
6328
  return null;
5841
6329
  }
5842
6330
  const newNodeType = getInputType(schema);
5843
6331
  if (!isDefined(nodeValue) && (jsf.formOptions.setSchemaDefaults === true ||
5844
6332
  (jsf.formOptions.setSchemaDefaults === 'auto' && isEmpty(jsf.formValues)))) {
5845
- nodeValue = JsonPointer.get(jsf.schema, schemaPointer + '/default');
6333
+ nodeValue = JsonPointer.get(jsSchema, schemaPointer + '/default');
5846
6334
  }
5847
6335
  let newNode = {
5848
6336
  _id: forRefLibrary ? null : uniqueId(),
@@ -5850,7 +6338,7 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
5850
6338
  dataPointer: JsonPointer.toGenericPointer(dataPointer, jsf.arrayMap),
5851
6339
  dataType: schema.type || (hasOwn(schema, '$ref') ? '$ref' : null),
5852
6340
  options: {},
5853
- required: isInputRequired(jsf.schema, schemaPointer),
6341
+ required: isInputRequired(jsSchema, schemaPointer),
5854
6342
  type: newNodeType,
5855
6343
  widget: widgetLibrary.getWidget(newNodeType),
5856
6344
  };
@@ -5868,6 +6356,7 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
5868
6356
  if (!jsf.dataMap.has(shortDataPointer)) {
5869
6357
  jsf.dataMap.set(shortDataPointer, new Map());
5870
6358
  }
6359
+ updateInputOptions(newNode, schema, jsf);
5871
6360
  const nodeDataMap = jsf.dataMap.get(shortDataPointer);
5872
6361
  if (!nodeDataMap.has('inputType')) {
5873
6362
  nodeDataMap.set('schemaPointer', schemaPointer);
@@ -5875,7 +6364,7 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
5875
6364
  nodeDataMap.set('widget', newNode.widget);
5876
6365
  nodeDataMap.set('disabled', !!newNode.options.disabled);
5877
6366
  }
5878
- updateInputOptions(newNode, schema, jsf);
6367
+ //updateInputOptions(newNode, schema, jsf);
5879
6368
  if (!newNode.options.title && newNode.name && !/^\d+$/.test(newNode.name)) {
5880
6369
  newNode.options.title = fixTitle(newNode.name);
5881
6370
  }
@@ -5903,6 +6392,7 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
5903
6392
  '/properties/' + key : '/additionalProperties';
5904
6393
  const innerItem = buildLayoutFromSchema(jsf, widgetLibrary, isObject(nodeValue) ? nodeValue[key] : null, schemaPointer + keySchemaPointer, dataPointer + '/' + key, false, null, null, forRefLibrary, dataPointerPrefix);
5905
6394
  if (innerItem) {
6395
+ innerItem.schemaPointer = schemaPointer + keySchemaPointer;
5906
6396
  if (isInputRequired(schema, '/' + key)) {
5907
6397
  innerItem.options.required = true;
5908
6398
  jsf.fieldsRequired = true;
@@ -5910,6 +6400,102 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
5910
6400
  newSection.push(innerItem);
5911
6401
  }
5912
6402
  });
6403
+ //treat allOf the same as any of but need to add an extra
6404
+ //condition for which anyOf item is to be rendered
6405
+ ["allOf", "anyOf", "oneOf"].forEach(ofType => {
6406
+ if (hasOwn(schema, ofType) && isArray(schema[ofType])) {
6407
+ let outerOneOfItem;
6408
+ if (ofType == "oneOf") {
6409
+ outerOneOfItem = buildLayoutFromSchema(jsf, widgetLibrary, schema.oneOf, //{type:"tabarray",items:schema.oneOf},
6410
+ "/", //schemaPointer + `/${ofType}`,
6411
+ dataPointer, false, null, null, forRefLibrary, dataPointerPrefix,
6412
+ //{type:"tabarray",items:schema.oneOf,oneOf:schema.oneOf}
6413
+ { type: "one-of", items: schema.oneOf, oneOf: schema.oneOf });
6414
+ //outerItem.items=cloneDeep(newSection);
6415
+ //newSection.length=0;
6416
+ newSection.push(outerOneOfItem);
6417
+ }
6418
+ schema[ofType].forEach((ofItem, ind) => {
6419
+ const keySchemaPointer = `/${ofType}/${ind}`;
6420
+ const innerItem = buildLayoutFromSchema(jsf, widgetLibrary, ofItem, schemaPointer + keySchemaPointer, dataPointer, false, null, null, ofType == "oneOf" /*forRefLibrary*/, dataPointerPrefix);
6421
+ if (innerItem) {
6422
+ //newSection.push(innerItem);
6423
+ if (innerItem.items) {
6424
+ innerItem.items.forEach(innerItemLevel2 => {
6425
+ const l2SchemaPointer = hasOwn(ofItem, 'properties') ?
6426
+ '/properties/' + innerItemLevel2.name : innerItemLevel2.name;
6427
+ //innerItemLevel2.oneOfPointer = schemaPointer + keySchemaPointer + l2SchemaPointer;
6428
+ // innerItemLevel2.schemaPointer=innerItemLevel2.schemaPointer;
6429
+ innerItemLevel2.oneOfPointer = innerItemLevel2.schemaPointer;
6430
+ });
6431
+ }
6432
+ //TODO review-will never reach here if forRefLibrary==true
6433
+ if (isArray(innerItem)) {
6434
+ let outerOneOfItemTpl = cloneDeep(newNode);
6435
+ outerOneOfItemTpl;
6436
+ innerItem.forEach(item => {
6437
+ const l2SchemaPointer = hasOwn(ofItem, 'properties') ?
6438
+ '/properties/' + item.name : item.name;
6439
+ if (outerOneOfItem) {
6440
+ ////item.oneOfPointer = schemaPointer + keySchemaPointer + l2SchemaPointer;
6441
+ //schemaPointer + keySchemaPointer + item.dataPointer;
6442
+ ////item.schemaPointer=item.oneOfPointer;
6443
+ /*
6444
+ outerOneOfItem.items=outerOneOfItem.items||[];
6445
+ outerOneOfItem.items.push(item);
6446
+ */
6447
+ outerOneOfItemTpl.items = outerOneOfItemTpl.items || [];
6448
+ outerOneOfItemTpl.items.push(item);
6449
+ }
6450
+ else {
6451
+ newSection.push(item);
6452
+ }
6453
+ });
6454
+ if (outerOneOfItem) {
6455
+ outerOneOfItem.items = outerOneOfItem.items || [];
6456
+ outerOneOfItem.items.push(outerOneOfItemTpl);
6457
+ }
6458
+ //TODO test-might not work for more than 2 levels of nesting
6459
+ }
6460
+ else {
6461
+ if (outerOneOfItem) {
6462
+ innerItem.oneOfPointer = schemaPointer + keySchemaPointer; // + innerItem.dataPointer;
6463
+ ////innerItem.schemaPointer=innerItem.oneOfPointer;
6464
+ outerOneOfItem.items = outerOneOfItem.items || [];
6465
+ outerOneOfItem.items.push(innerItem);
6466
+ }
6467
+ else {
6468
+ newSection.push(innerItem);
6469
+ }
6470
+ }
6471
+ }
6472
+ });
6473
+ }
6474
+ });
6475
+ if (hasOwn(schema, "if")) {
6476
+ ["then", "else"].forEach(con => {
6477
+ if (hasOwn(schema, con)) {
6478
+ const keySchemaPointer = `/${con}`;
6479
+ const negateClause = con == "else";
6480
+ const innerItem = buildLayoutFromSchema(jsf, widgetLibrary, nodeValue.then, schemaPointer + keySchemaPointer, dataPointer, false, null, null, forRefLibrary, dataPointerPrefix);
6481
+ if (innerItem) {
6482
+ applyITEConditions(innerItem, schemaPointer, keySchemaPointer, negateClause);
6483
+ if (isArray(innerItem)) {
6484
+ innerItem.forEach(item => {
6485
+ //item.schemaPointer = schemaPointer + keySchemaPointer + item.dataPointer;
6486
+ //item.options.condition = convertJSONSchemaIfToCondition(schema, negateClause);
6487
+ newSection.push(item);
6488
+ });
6489
+ }
6490
+ else {
6491
+ //innerItem.schemaPointer = schemaPointer + keySchemaPointer + innerItem.dataPointer;
6492
+ //innerItem.options.condition = convertJSONSchemaIfToCondition(schema, negateClause);
6493
+ newSection.push(innerItem);
6494
+ }
6495
+ }
6496
+ }
6497
+ });
6498
+ }
5913
6499
  if (dataPointer === '' && !forRefLibrary) {
5914
6500
  newNode = newSection;
5915
6501
  }
@@ -5926,7 +6512,7 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
5926
6512
  newNode.items = [];
5927
6513
  newNode.options.maxItems = Math.min(schema.maxItems || 1000, newNode.options.maxItems || 1000);
5928
6514
  newNode.options.minItems = Math.max(schema.minItems || 0, newNode.options.minItems || 0);
5929
- if (!newNode.options.minItems && isInputRequired(jsf.schema, schemaPointer)) {
6515
+ if (!newNode.options.minItems && isInputRequired(jsSchema, schemaPointer)) {
5930
6516
  newNode.options.minItems = 1;
5931
6517
  }
5932
6518
  if (!hasOwn(newNode.options, 'listItems')) {
@@ -6059,7 +6645,7 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
6059
6645
  }
6060
6646
  else if (newNode.dataType === '$ref') {
6061
6647
  const schemaRef = JsonPointer.compile(schema.$ref);
6062
- const dataRef = JsonPointer.toDataPointer(schemaRef, jsf.schema);
6648
+ const dataRef = JsonPointer.toDataPointer(schemaRef, jsSchema);
6063
6649
  let buttonText = '';
6064
6650
  // Get newNode title
6065
6651
  if (newNode.options.add) {
@@ -6071,7 +6657,7 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
6071
6657
  // If newNode doesn't have a title, look for title of parent array item
6072
6658
  }
6073
6659
  else {
6074
- const parentSchema = JsonPointer.get(jsf.schema, schemaPointer, 0, -1);
6660
+ const parentSchema = JsonPointer.get(jsSchema, schemaPointer, 0, -1);
6075
6661
  if (hasOwn(parentSchema, 'title')) {
6076
6662
  buttonText = 'Add to ' + parentSchema.title;
6077
6663
  }
@@ -6089,9 +6675,9 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
6089
6675
  removable: false,
6090
6676
  title: buttonText,
6091
6677
  });
6092
- if (isNumber(JsonPointer.get(jsf.schema, schemaPointer, 0, -1).maxItems)) {
6678
+ if (isNumber(JsonPointer.get(jsSchema, schemaPointer, 0, -1).maxItems)) {
6093
6679
  newNode.options.maxItems =
6094
- JsonPointer.get(jsf.schema, schemaPointer, 0, -1).maxItems;
6680
+ JsonPointer.get(jsSchema, schemaPointer, 0, -1).maxItems;
6095
6681
  }
6096
6682
  // Add layout template to layoutRefLibrary
6097
6683
  if (dataRef.length) {
@@ -6112,6 +6698,32 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
6112
6698
  }
6113
6699
  }
6114
6700
  }
6701
+ else if (newNode.type === 'if') {
6702
+ const newSection = [];
6703
+ ["then", "else"].forEach(con => {
6704
+ if (hasOwn(schema, con)) {
6705
+ const keySchemaPointer = `/${con}`;
6706
+ const negateClause = con == "else";
6707
+ const innerItem = buildLayoutFromSchema(jsf, widgetLibrary, nodeValue.then, schemaPointer + keySchemaPointer, dataPointer, false, null, null, forRefLibrary, dataPointerPrefix);
6708
+ if (innerItem) {
6709
+ applyITEConditions(innerItem, schemaPointer, keySchemaPointer, negateClause);
6710
+ if (isArray(innerItem)) {
6711
+ innerItem.forEach(item => {
6712
+ //item.schemaPointer = schemaPointer + keySchemaPointer + item.dataPointer;
6713
+ //item.options.condition = convertJSONSchemaIfToCondition(schema, negateClause);
6714
+ newSection.push(item);
6715
+ newNode = newSection;
6716
+ });
6717
+ }
6718
+ else {
6719
+ //innerItem.schemaPointer = schemaPointer + keySchemaPointer + innerItem.dataPointer;
6720
+ //innerItem.options.condition = convertJSONSchemaIfToCondition(schema, negateClause);
6721
+ newNode = innerItem;
6722
+ }
6723
+ }
6724
+ }
6725
+ });
6726
+ }
6115
6727
  return newNode;
6116
6728
  }
6117
6729
  /**
@@ -6378,6 +6990,32 @@ class JsonSchemaFormService {
6378
6990
  setSortableOptions(value) {
6379
6991
  this.sortableOptionsSubject.next(value); // Update the sortable options value
6380
6992
  }
6993
+ createAjvInstance(ajvOptions) {
6994
+ let ajvInstance = new Ajv2019(ajvOptions);
6995
+ ajvInstance.addMetaSchema(jsonDraft6);
6996
+ ajvInstance.addMetaSchema(jsonDraft7);
6997
+ addFormats(ajvInstance);
6998
+ return ajvInstance;
6999
+ }
7000
+ createAndRegisterAjvInstance(ajvOptions, name) {
7001
+ const intanceName = name || `ajv_${Date.now()}`;
7002
+ if (this.ajvRegistry[intanceName]) {
7003
+ throw new Error(`ajv instance with name:'${intanceName}' has already been registered`);
7004
+ }
7005
+ const ajvInstance = this.createAjvInstance(ajvOptions);
7006
+ this.ajvRegistry[intanceName] = {
7007
+ name: intanceName,
7008
+ ajvInstance: ajvInstance,
7009
+ ajvValidator: null
7010
+ };
7011
+ return this.ajvRegistry[intanceName];
7012
+ }
7013
+ getAjvInstance(name = 'default') {
7014
+ return this.ajvRegistry[name].ajvInstance;
7015
+ }
7016
+ getAjvValidator(name = 'default') {
7017
+ return this.ajvRegistry[name]?.ajvValidator;
7018
+ }
6381
7019
  constructor() {
6382
7020
  this.JsonFormCompatibility = false;
6383
7021
  this.ReactJsonSchemaFormCompatibility = false;
@@ -6385,10 +7023,11 @@ class JsonSchemaFormService {
6385
7023
  this.tpldata = {};
6386
7024
  this.ajvOptions = {
6387
7025
  allErrors: true,
6388
- validateFormats: false,
7026
+ //validateFormats:false,
6389
7027
  strict: false
6390
7028
  };
6391
7029
  this.ajv = new Ajv2019(this.ajvOptions); // AJV: Another JSON Schema Validator
7030
+ //Being replaced by getAjvValidator()
6392
7031
  this.validateFormData = null; // Compiled AJV function to validate active form's schema
6393
7032
  this.formValues = {}; // Internal form data (may not have correct types)
6394
7033
  this.data = {}; // Output form data (formValues, formatted with correct data types)
@@ -6462,6 +7101,7 @@ class JsonSchemaFormService {
6462
7101
  validationMessages: {} // set by setLanguage()
6463
7102
  }
6464
7103
  };
7104
+ //TODO-review,may not be needed as sortablejs replaces dnd
6465
7105
  //this has been added to enable or disable the dragabble state of a component
6466
7106
  //using the OrderableDirective, mainly when an <input type="range">
6467
7107
  //elements are present, as the draggable attribute makes it difficult to
@@ -6474,9 +7114,20 @@ class JsonSchemaFormService {
6474
7114
  //nxt-sortablejs and sortablejs
6475
7115
  this.sortableOptionsSubject = new BehaviorSubject({ disabled: false }); // Default value true
6476
7116
  this.sortableOptions$ = this.sortableOptionsSubject.asObservable();
7117
+ this.ajvRegistry = {};
6477
7118
  this.setLanguage(this.language);
6478
7119
  this.ajv.addMetaSchema(jsonDraft6);
6479
7120
  this.ajv.addMetaSchema(jsonDraft7);
7121
+ addFormats(this.ajv);
7122
+ this.ajvRegistry['default'] = { name: 'default', ajvInstance: this.ajv, ajvValidator: null };
7123
+ console.log(this.ajvRegistry);
7124
+ // Add custom 'duration' format using a regex
7125
+ /*
7126
+ this.ajv.addFormat("duration", {
7127
+ type: "string",
7128
+ validate: (duration) => /^P(?!$)(\d+Y)?(\d+M)?(\d+D)?(T(\d+H)?(\d+M)?(\d+S)?)?$/.test(duration)
7129
+ });
7130
+ */
6480
7131
  }
6481
7132
  ngOnDestroy() {
6482
7133
  this.fcValueChangesSubs?.unsubscribe();
@@ -6515,7 +7166,7 @@ class JsonSchemaFormService {
6515
7166
  this.ReactJsonSchemaFormCompatibility = false;
6516
7167
  this.AngularSchemaFormCompatibility = false;
6517
7168
  this.tpldata = {};
6518
- this.validateFormData = null;
7169
+ this.validateFormData = null; //Being replaced by getAjvValidator()
6519
7170
  this.formValues = {};
6520
7171
  this.schema = {};
6521
7172
  this.layout = [];
@@ -6534,6 +7185,8 @@ class JsonSchemaFormService {
6534
7185
  this.schemaRefLibrary = {};
6535
7186
  this.templateRefLibrary = {};
6536
7187
  this.formOptions = cloneDeep(this.defaultFormOptions);
7188
+ this.ajvRegistry = {};
7189
+ this.ajvRegistry['default'] = { name: 'default', ajvInstance: this.ajv, ajvValidator: null };
6537
7190
  }
6538
7191
  /**
6539
7192
  * 'buildRemoteError' function
@@ -6565,10 +7218,10 @@ class JsonSchemaFormService {
6565
7218
  }
6566
7219
  });
6567
7220
  }
6568
- validateData(newValue, updateSubscriptions = true) {
7221
+ validateData(newValue, updateSubscriptions = true, ajvInstanceName = 'default') {
6569
7222
  // Format raw form data to correct data types
6570
7223
  this.data = formatFormData(newValue, this.dataMap, this.dataRecursiveRefMap, this.arrayMap, this.formOptions.returnEmptyFields);
6571
- this.isValid = this.validateFormData(this.data);
7224
+ this.isValid = this.getAjvValidator(ajvInstanceName)(this.data);
6572
7225
  this.validData = this.isValid ? this.data : null;
6573
7226
  const compileErrors = (errors) => {
6574
7227
  const compiledErrors = {};
@@ -6583,8 +7236,9 @@ class JsonSchemaFormService {
6583
7236
  });
6584
7237
  return compiledErrors;
6585
7238
  };
6586
- this.ajvErrors = this.validateFormData.errors;
6587
- this.validationErrors = compileErrors(this.validateFormData.errors);
7239
+ //TODO:store avjErrors per ajvInstance in registry
7240
+ this.ajvErrors = this.getAjvValidator(ajvInstanceName).errors;
7241
+ this.validationErrors = compileErrors(this.ajvErrors);
6588
7242
  if (updateSubscriptions) {
6589
7243
  this.dataChanges.next(this.data);
6590
7244
  this.isValidChanges.next(this.isValid);
@@ -6594,16 +7248,16 @@ class JsonSchemaFormService {
6594
7248
  buildFormGroupTemplate(formValues = null, setValues = true) {
6595
7249
  this.formGroupTemplate = buildFormGroupTemplate(this, formValues, setValues);
6596
7250
  }
6597
- buildFormGroup() {
7251
+ buildFormGroup(ajvInstanceName) {
6598
7252
  this.formGroup = buildFormGroup(this.formGroupTemplate);
6599
7253
  if (this.formGroup) {
6600
- this.compileAjvSchema();
6601
- this.validateData(this.formGroup.value);
7254
+ this.compileAjvSchema(ajvInstanceName);
7255
+ this.validateData(this.formGroup.value, true, ajvInstanceName);
6602
7256
  // Set up observables to emit data and validation info when form data changes
6603
7257
  if (this.formValueSubscription) {
6604
7258
  this.formValueSubscription.unsubscribe();
6605
7259
  }
6606
- this.formValueSubscription = this.formGroup.valueChanges.subscribe(formValue => this.validateData(formValue));
7260
+ this.formValueSubscription = this.formGroup.valueChanges.subscribe(formValue => this.validateData(formValue, true, ajvInstanceName));
6607
7261
  }
6608
7262
  }
6609
7263
  buildLayout(widgetLibrary) {
@@ -6632,15 +7286,17 @@ class JsonSchemaFormService {
6632
7286
  });
6633
7287
  }
6634
7288
  }
6635
- compileAjvSchema() {
6636
- if (!this.validateFormData) {
7289
+ compileAjvSchema(ajvInstanceName = 'default') {
7290
+ let ajvValidator = this.getAjvValidator(ajvInstanceName);
7291
+ if (!ajvValidator) {
6637
7292
  // if 'ui:order' exists in properties, move it to root before compiling with ajv
6638
7293
  if (Array.isArray(this.schema.properties['ui:order'])) {
6639
7294
  this.schema['ui:order'] = this.schema.properties['ui:order'];
6640
7295
  delete this.schema.properties['ui:order'];
6641
7296
  }
6642
- this.ajv.removeSchema(this.schema);
6643
- this.validateFormData = this.ajv.compile(this.schema);
7297
+ this.getAjvInstance(ajvInstanceName).removeSchema(this.schema);
7298
+ ajvValidator = this.getAjvInstance(ajvInstanceName).compile(this.schema);
7299
+ this.ajvRegistry[ajvInstanceName].ajvValidator = ajvValidator;
6644
7300
  }
6645
7301
  }
6646
7302
  buildSchemaFromData(data, requireAllFields = false) {
@@ -6802,12 +7458,24 @@ class JsonSchemaFormService {
6802
7458
  if (!isObject(ctx)) {
6803
7459
  return false;
6804
7460
  }
7461
+ const layoutNode = ctx.layoutNode();
6805
7462
  if (isEmpty(ctx.options)) {
6806
- ctx.options = !isEmpty((ctx.layoutNode() || {}).options)
6807
- ? ctx.layoutNode().options
7463
+ ctx.options = !isEmpty((layoutNode || {}).options)
7464
+ ? layoutNode.options
6808
7465
  : cloneDeep(this.formOptions);
6809
7466
  }
6810
7467
  ctx.formControl = this.getFormControl(ctx);
7468
+ //introduced to check if the node is part of ITE conditional
7469
+ //then change or add the control
7470
+ if (layoutNode?.schemaPointer && layoutNode.isITEItem ||
7471
+ (layoutNode?.schemaPointer && layoutNode?.oneOfPointer)) {
7472
+ //before changing control, need to set the new data type for data formatting
7473
+ const schemaType = this.dataMap.get(layoutNode?.dataPointer).get("schemaType");
7474
+ if (schemaType != layoutNode.dataType) {
7475
+ this.dataMap.get(layoutNode?.dataPointer).set("schemaType", layoutNode.dataType);
7476
+ }
7477
+ this.setFormControl(ctx, ctx.formControl);
7478
+ }
6811
7479
  ctx.boundControl = bind && !!ctx.formControl;
6812
7480
  if (ctx.formControl) {
6813
7481
  ctx.controlName = this.getFormControlName(ctx);
@@ -6826,27 +7494,40 @@ class JsonSchemaFormService {
6826
7494
  ? null
6827
7495
  : this.formatErrors(ctx.formControl.errors, ctx.options.validationMessages)));
6828
7496
  this.fcValueChangesSubs = ctx.formControl.valueChanges.subscribe(value => {
6829
- //commented out to revert back to previous commits
6830
- //as seems to be causing some issues
6831
- /*
6832
- if (!!value) {
6833
- ctx.controlValue = value;
6834
- }
6835
- */
6836
- //TODO-test,this is the original code
6837
7497
  if (!isEqual$1(ctx.controlValue, value)) {
6838
7498
  ctx.controlValue = value;
6839
7499
  }
6840
7500
  });
6841
7501
  }
6842
7502
  else {
6843
- ctx.controlName = ctx.layoutNode().name;
6844
- ctx.controlValue = ctx.layoutNode().value || null;
7503
+ ctx.controlName = layoutNode.name;
7504
+ ctx.controlValue = layoutNode.value || null;
6845
7505
  const dataPointer = this.getDataPointer(ctx);
6846
7506
  if (bind && dataPointer) {
6847
7507
  console.error(`warning: control "${dataPointer}" is not bound to the Angular FormGroup.`);
6848
7508
  }
6849
7509
  }
7510
+ //if this is a ITE conditional field, the value would not have been
7511
+ //set, as the control would only be initialized when the condition is true
7512
+ //TODO-review need to decide which of the data sets between data,formValues and default
7513
+ //to use for the value
7514
+ if (ctx.options?.condition || layoutNode?.oneOfPointer) {
7515
+ const dataPointer = this.getDataPointer(ctx);
7516
+ const controlValue = ctx.formControl.value;
7517
+ const dataValue = JsonPointer.has(this.data, dataPointer) ?
7518
+ JsonPointer.get(this.data, dataPointer) : undefined;
7519
+ const formValue = JsonPointer.has(this.formValues, dataPointer) ?
7520
+ JsonPointer.get(this.formValues, dataPointer) : undefined;
7521
+ const schemaDefault = ctx.options?.default;
7522
+ //if initial formValues was supplied and controlValue matches formValue then likely
7523
+ //control was initially created with the formValue then set value to data value
7524
+ //if no formValues was supplied and controlValue matches schemaDefault then likely
7525
+ //control was initially created with the default then set value to data value
7526
+ const value = this.formValues && isEqual$1(formValue, controlValue) ? dataValue
7527
+ : !this.formValues && isEqual$1(schemaDefault, controlValue) ? dataValue
7528
+ : schemaDefault;
7529
+ ctx.formControl?.patchValue(value);
7530
+ }
6850
7531
  return ctx.boundControl;
6851
7532
  }
6852
7533
  formatErrors(errors, validationMessages = {}) {
@@ -6928,13 +7609,50 @@ class JsonSchemaFormService {
6928
7609
  }
6929
7610
  formArray.markAsDirty();
6930
7611
  }
7612
+ updateArrayMultiSelectList(ctx, selectList) {
7613
+ this.updateArrayCheckboxList(ctx, selectList);
7614
+ /* const formArray = <UntypedFormArray>this.getFormControl(ctx);
7615
+
7616
+ // Remove all existing items
7617
+ while (formArray.value.length) {
7618
+ formArray.removeAt(0);
7619
+ }
7620
+
7621
+ // Re-add an item for each checked box
7622
+ const refPointer = removeRecursiveReferences(
7623
+ ctx.layoutNode().dataPointer + '/-',
7624
+ this.dataRecursiveRefMap,
7625
+ this.arrayMap
7626
+ );
7627
+ for (const selectItem of selectList) {
7628
+ if (selectItem.value) {
7629
+ const newFormControl = buildFormGroup(
7630
+ this.templateRefLibrary[refPointer]
7631
+ );
7632
+ newFormControl.setValue(selectItem.value);
7633
+ formArray.push(newFormControl);
7634
+ }
7635
+ }
7636
+ formArray.markAsDirty();
7637
+ */
7638
+ }
6931
7639
  getFormControl(ctx) {
6932
7640
  if (!ctx || !ctx.layoutNode ||
6933
7641
  !isDefined(ctx.layoutNode().dataPointer) ||
6934
7642
  ctx.layoutNode().type === '$ref') {
6935
7643
  return null;
6936
7644
  }
6937
- return getControl(this.formGroup, this.getDataPointer(ctx));
7645
+ const schemaPointer = ctx.layoutNode()?.isITEItem ? ctx.layoutNode()?.schemaPointer : null;
7646
+ const oneOfPointer = ctx.layoutNode()?.oneOfPointer;
7647
+ return getControl(this.formGroup, this.getDataPointer(ctx), false, schemaPointer || oneOfPointer);
7648
+ }
7649
+ setFormControl(ctx, control) {
7650
+ if (!ctx || !ctx.layoutNode ||
7651
+ !isDefined(ctx.layoutNode().dataPointer) ||
7652
+ ctx.layoutNode().type === '$ref') {
7653
+ return null;
7654
+ }
7655
+ return setControl(this.formGroup, this.getDataPointer(ctx), control);
6938
7656
  }
6939
7657
  getFormControlValue(ctx) {
6940
7658
  if (!ctx || !ctx.layoutNode ||
@@ -6942,14 +7660,18 @@ class JsonSchemaFormService {
6942
7660
  ctx.layoutNode().type === '$ref') {
6943
7661
  return null;
6944
7662
  }
6945
- const control = getControl(this.formGroup, this.getDataPointer(ctx));
7663
+ const schemaPointer = ctx.layoutNode()?.isITEItem ? ctx.layoutNode()?.schemaPointer : null;
7664
+ const oneOfPointer = ctx.layoutNode()?.oneOfPointer;
7665
+ const control = getControl(this.formGroup, this.getDataPointer(ctx), false, schemaPointer || oneOfPointer);
6946
7666
  return control ? control.value : null;
6947
7667
  }
6948
7668
  getFormControlGroup(ctx) {
6949
7669
  if (!ctx || !ctx.layoutNode || !isDefined(ctx.layoutNode().dataPointer)) {
6950
7670
  return null;
6951
7671
  }
6952
- return getControl(this.formGroup, this.getDataPointer(ctx), true);
7672
+ const schemaPointer = ctx.layoutNode()?.isITEItem ? ctx.layoutNode()?.schemaPointer : null;
7673
+ const oneOfPointer = ctx.layoutNode()?.oneOfPointer;
7674
+ return getControl(this.formGroup, this.getDataPointer(ctx), true, schemaPointer || oneOfPointer);
6953
7675
  }
6954
7676
  getFormControlName(ctx) {
6955
7677
  if (!ctx || !ctx.layoutNode ||
@@ -7202,13 +7924,15 @@ class AddReferenceComponent {
7202
7924
  }
7203
7925
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AddReferenceComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7204
7926
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: AddReferenceComponent, selector: "add-reference-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
7205
- <button *ngIf="showAddButton"
7206
- [class]="options?.fieldHtmlClass || ''"
7207
- [disabled]="options?.readonly"
7208
- (click)="addItem($event)">
7209
- <span *ngIf="options?.icon" [class]="options?.icon"></span>
7210
- <span *ngIf="options?.title" [innerHTML]="buttonText"></span>
7211
- </button>`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.Default }); }
7927
+ <section [class]="options?.htmlClass || ''" align="end">
7928
+ <button *ngIf="showAddButton"
7929
+ [class]="options?.fieldHtmlClass || ''" class="sortable-filter sortable-fixed"
7930
+ [disabled]="options?.readonly"
7931
+ (click)="addItem($event)">
7932
+ <span *ngIf="options?.icon" [class]="options?.icon"></span>
7933
+ <span *ngIf="options?.title" [innerHTML]="buttonText"></span>
7934
+ </button>
7935
+ </section>`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.Default }); }
7212
7936
  }
7213
7937
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AddReferenceComponent, decorators: [{
7214
7938
  type: Component,
@@ -7216,13 +7940,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
7216
7940
  // tslint:disable-next-line:component-selector
7217
7941
  selector: 'add-reference-widget',
7218
7942
  template: `
7219
- <button *ngIf="showAddButton"
7220
- [class]="options?.fieldHtmlClass || ''"
7221
- [disabled]="options?.readonly"
7222
- (click)="addItem($event)">
7223
- <span *ngIf="options?.icon" [class]="options?.icon"></span>
7224
- <span *ngIf="options?.title" [innerHTML]="buttonText"></span>
7225
- </button>`,
7943
+ <section [class]="options?.htmlClass || ''" align="end">
7944
+ <button *ngIf="showAddButton"
7945
+ [class]="options?.fieldHtmlClass || ''" class="sortable-filter sortable-fixed"
7946
+ [disabled]="options?.readonly"
7947
+ (click)="addItem($event)">
7948
+ <span *ngIf="options?.icon" [class]="options?.icon"></span>
7949
+ <span *ngIf="options?.title" [innerHTML]="buttonText"></span>
7950
+ </button>
7951
+ </section>`,
7226
7952
  changeDetection: ChangeDetectionStrategy.Default,
7227
7953
  }]
7228
7954
  }] });
@@ -7248,6 +7974,9 @@ class ButtonComponent {
7248
7974
  this.jsf.updateValue(this, event.target.value);
7249
7975
  }
7250
7976
  }
7977
+ ngOnDestroy() {
7978
+ this.jsf.updateValue(this, null);
7979
+ }
7251
7980
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7252
7981
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: ButtonComponent, selector: "button-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
7253
7982
  <div
@@ -7292,6 +8021,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
7292
8021
  }]
7293
8022
  }] });
7294
8023
 
8024
+ ///NB issue caused by sortablejs when it its destroyed
8025
+ //this mainly affects checkboxes coupled with conditions
8026
+ //-the value is rechecked
8027
+ //-see https://github.com/SortableJS/Sortable/issues/1052#issuecomment-369613072
7295
8028
  class CheckboxComponent {
7296
8029
  constructor() {
7297
8030
  this.jsf = inject(JsonSchemaFormService);
@@ -7307,7 +8040,8 @@ class CheckboxComponent {
7307
8040
  this.options = this.layoutNode().options || {};
7308
8041
  this.jsf.initializeControl(this);
7309
8042
  if (this.controlValue === null || this.controlValue === undefined) {
7310
- this.controlValue = this.options.title;
8043
+ this.controlValue = false;
8044
+ this.jsf.updateValue(this, this.falseValue);
7311
8045
  }
7312
8046
  }
7313
8047
  updateValue(event) {
@@ -7317,6 +8051,9 @@ class CheckboxComponent {
7317
8051
  get isChecked() {
7318
8052
  return this.jsf.getFormControlValue(this) === this.trueValue;
7319
8053
  }
8054
+ ngOnDestroy() {
8055
+ this.jsf.updateValue(this, null);
8056
+ }
7320
8057
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: CheckboxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7321
8058
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: CheckboxComponent, selector: "checkbox-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
7322
8059
  <label
@@ -7334,7 +8071,7 @@ class CheckboxComponent {
7334
8071
  type="checkbox">
7335
8072
  <input *ngIf="!boundControl"
7336
8073
  [attr.aria-describedby]="'control' + layoutNode()?._id + 'Status'"
7337
- [checked]="isChecked ? 'checked' : null"
8074
+ [checked]="isChecked"
7338
8075
  [class]="(options?.fieldHtmlClass || '') + (isChecked ?
7339
8076
  (' ' + (options?.activeClass || '') + ' ' + (options?.style?.selected || '')) :
7340
8077
  (' ' + (options?.style?.unselected || '')))"
@@ -7371,7 +8108,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
7371
8108
  type="checkbox">
7372
8109
  <input *ngIf="!boundControl"
7373
8110
  [attr.aria-describedby]="'control' + layoutNode()?._id + 'Status'"
7374
- [checked]="isChecked ? 'checked' : null"
8111
+ [checked]="isChecked"
7375
8112
  [class]="(options?.fieldHtmlClass || '') + (isChecked ?
7376
8113
  (' ' + (options?.activeClass || '') + ' ' + (options?.style?.selected || '')) :
7377
8114
  (' ' + (options?.style?.unselected || '')))"
@@ -7421,6 +8158,13 @@ class CheckboxesComponent {
7421
8158
  this.jsf.updateArrayCheckboxList(this, this.checkboxList);
7422
8159
  }
7423
8160
  }
8161
+ //TODO review this
8162
+ ngOnDestroy() {
8163
+ //this.jsf.updateValue(this, null);
8164
+ let nullVal = [];
8165
+ this.formControl.reset(nullVal);
8166
+ this.controlValue = null;
8167
+ }
7424
8168
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: CheckboxesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7425
8169
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: CheckboxesComponent, selector: "checkboxes-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
7426
8170
  <label *ngIf="options?.title"
@@ -7546,6 +8290,9 @@ class FileComponent {
7546
8290
  updateValue(event) {
7547
8291
  this.jsf.updateValue(this, event.target.value);
7548
8292
  }
8293
+ ngOnDestroy() {
8294
+ this.jsf.updateValue(this, null);
8295
+ }
7549
8296
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FileComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7550
8297
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: FileComponent, selector: "file-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: ``, isInline: true }); }
7551
8298
  }
@@ -7570,6 +8317,9 @@ class HiddenComponent {
7570
8317
  ngOnInit() {
7571
8318
  this.jsf.initializeControl(this);
7572
8319
  }
8320
+ ngOnDestroy() {
8321
+ this.jsf.updateValue(this, null);
8322
+ }
7573
8323
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HiddenComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7574
8324
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: HiddenComponent, selector: "hidden-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
7575
8325
  <input *ngIf="boundControl"
@@ -7625,6 +8375,9 @@ class InputComponent {
7625
8375
  updateValue(event) {
7626
8376
  this.jsf.updateValue(this, event.target.value);
7627
8377
  }
8378
+ ngOnDestroy() {
8379
+ this.jsf.updateValue(this, null);
8380
+ }
7628
8381
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: InputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7629
8382
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: InputComponent, selector: "input-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
7630
8383
  <div [class]="options?.htmlClass || ''" class="sortable-filter" >
@@ -7804,6 +8557,9 @@ class NumberComponent {
7804
8557
  updateValue(event) {
7805
8558
  this.jsf.updateValue(this, event.target.value);
7806
8559
  }
8560
+ ngOnDestroy() {
8561
+ this.jsf.updateValue(this, null);
8562
+ }
7807
8563
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NumberComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7808
8564
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: NumberComponent, selector: "number-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "inputControl", first: true, predicate: ["inputControl"], descendants: true }, { propertyName: "div", first: true, predicate: ["divElt"], descendants: true }], ngImport: i0, template: `
7809
8565
  <div #divElt [class]="options?.htmlClass || ''" class="sortable-filter" >
@@ -7912,6 +8668,194 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
7912
8668
  args: ['divElt', {}]
7913
8669
  }] } });
7914
8670
 
8671
+ class SelectFrameworkComponent {
8672
+ constructor() {
8673
+ this.jsf = inject(JsonSchemaFormService);
8674
+ this.newComponent = null;
8675
+ this.layoutNode = input(undefined);
8676
+ this.layoutIndex = input(undefined);
8677
+ this.dataIndex = input(undefined);
8678
+ this.widgetContainer = viewChild('widgetContainer', { read: ViewContainerRef });
8679
+ }
8680
+ ngOnInit() {
8681
+ this.updateComponent();
8682
+ }
8683
+ ngOnChanges() {
8684
+ this.updateComponent();
8685
+ }
8686
+ updateComponent() {
8687
+ const widgetContainer = this.widgetContainer();
8688
+ if (widgetContainer && !this.newComponent && this.jsf.framework) {
8689
+ this.newComponent = widgetContainer.createComponent((this.jsf.framework));
8690
+ //TODO fix all deprecated calls and test
8691
+ //this.widgetContainer.createComponent<any>(this.jsf.framework)
8692
+ }
8693
+ if (this.newComponent) {
8694
+ for (const input of ['layoutNode', 'layoutIndex', 'dataIndex']) {
8695
+ this.newComponent.instance[input] = this[input];
8696
+ }
8697
+ }
8698
+ }
8699
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SelectFrameworkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8700
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "17.3.12", type: SelectFrameworkComponent, selector: "select-framework-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "widgetContainer", first: true, predicate: ["widgetContainer"], descendants: true, read: ViewContainerRef, isSignal: true }], usesOnChanges: true, ngImport: i0, template: `<div #widgetContainer></div>`, isInline: true }); }
8701
+ }
8702
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SelectFrameworkComponent, decorators: [{
8703
+ type: Component,
8704
+ args: [{
8705
+ // tslint:disable-next-line:component-selector
8706
+ selector: 'select-framework-widget',
8707
+ template: `<div #widgetContainer></div>`,
8708
+ }]
8709
+ }] });
8710
+
8711
+ class TabsComponent {
8712
+ constructor() {
8713
+ this.jsf = inject(JsonSchemaFormService);
8714
+ this.selectedItem = 0;
8715
+ this.showAddTab = true;
8716
+ this.layoutNode = input(undefined);
8717
+ this.layoutIndex = input(undefined);
8718
+ this.dataIndex = input(undefined);
8719
+ }
8720
+ ngOnInit() {
8721
+ this.options = this.layoutNode().options || {};
8722
+ if (this.options.selectedTab) {
8723
+ this.selectedItem = this.options.selectedTab;
8724
+ }
8725
+ this.itemCount = this.layoutNode().items.length - 1;
8726
+ this.updateControl();
8727
+ }
8728
+ select(index) {
8729
+ const layoutNode = this.layoutNode();
8730
+ if (layoutNode.items[index].type === '$ref') {
8731
+ this.itemCount = layoutNode.items.length;
8732
+ this.jsf.addItem({
8733
+ layoutNode: signal(layoutNode.items[index]),
8734
+ layoutIndex: signal(this.layoutIndex().concat(index)),
8735
+ dataIndex: signal(this.dataIndex().concat(index))
8736
+ });
8737
+ this.updateControl();
8738
+ }
8739
+ this.selectedItem = index;
8740
+ }
8741
+ updateControl() {
8742
+ const lastItem = this.layoutNode().items[this.layoutNode().items.length - 1];
8743
+ if (lastItem.type === '$ref' &&
8744
+ this.itemCount >= (lastItem.options.maxItems || 1000)) {
8745
+ this.showAddTab = false;
8746
+ }
8747
+ }
8748
+ setTabTitle(item, index) {
8749
+ return this.jsf.setArrayItemTitle(this, item, index);
8750
+ }
8751
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8752
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: TabsComponent, selector: "tabs-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
8753
+ <ul
8754
+ [class]="options?.labelHtmlClass || ''">
8755
+ <li *ngFor="let item of layoutNode()?.items; let i = index"
8756
+ [class]="(options?.itemLabelHtmlClass || '') + (selectedItem === i ?
8757
+ (' ' + (options?.activeClass || '') + ' ' + (options?.style?.selected || '')) :
8758
+ (' ' + options?.style?.unselected))"
8759
+ role="presentation"
8760
+ data-tabs>
8761
+ <a *ngIf="showAddTab || item.type !== '$ref'"
8762
+ [class]="'nav-link' + (selectedItem === i ? (' ' + options?.activeClass + ' ' + options?.style?.selected) :
8763
+ (' ' + options?.style?.unselected))"
8764
+ (click)="select(i)">
8765
+ <input type="radio" [value]="i" *ngIf="options?.tabMode=='oneOfMode'"
8766
+ name="tabSelection"
8767
+ [(ngModel)]="selectedItem"
8768
+ [class]="(options?.widget_radioClass || '')"
8769
+ [value]="i"
8770
+ (change)="select(i)"
8771
+ />
8772
+ {{setTabTitle(item, i)}}
8773
+ </a>
8774
+ </li>
8775
+ </ul>
8776
+
8777
+ <div *ngFor="let layoutItem of layoutNode()?.items; let i = index"
8778
+ [class]="(options?.htmlClass || '') + (selectedItem != i?' ngf-hidden':'') ">
8779
+ <!--for now the only difference between oneOfMode and the default
8780
+ is that oneOfMode uses the *ngIf="selectedItem === i" clause, which automatically
8781
+ destroys the tabs that are not rendered while default mode only hide them
8782
+ the upshot is that only the active tabs value will be used
8783
+ -->
8784
+ <ng-container *ngIf="options?.tabMode=='oneOfMode'">
8785
+ <select-framework-widget *ngIf="selectedItem === i"
8786
+ [class]="(options?.fieldHtmlClass || '') +
8787
+ ' ' + (options?.activeClass || '') +
8788
+ ' ' + (options?.style?.selected || '')"
8789
+ [dataIndex]="layoutNode()?.dataType === 'array' ? (dataIndex() || []).concat(i) : dataIndex()"
8790
+ [layoutIndex]="(layoutIndex() || []).concat(i)"
8791
+ [layoutNode]="layoutItem"></select-framework-widget>
8792
+ </ng-container>
8793
+ <ng-container *ngIf="options?.tabMode !='oneOfMode'">
8794
+ <select-framework-widget
8795
+ [class]="(options?.fieldHtmlClass || '') +
8796
+ ' ' + (options?.activeClass || '') +
8797
+ ' ' + (options?.style?.selected || '')"
8798
+ [dataIndex]="layoutNode()?.dataType === 'array' ? (dataIndex() || []).concat(i) : dataIndex()"
8799
+ [layoutIndex]="(layoutIndex() || []).concat(i)"
8800
+ [layoutNode]="layoutItem"></select-framework-widget>
8801
+ </ng-container>
8802
+ </div>`, isInline: true, styles: ["a{cursor:pointer}.ngf-hidden{display:none}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: SelectFrameworkComponent, selector: "select-framework-widget", inputs: ["layoutNode", "layoutIndex", "dataIndex"] }] }); }
8803
+ }
8804
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TabsComponent, decorators: [{
8805
+ type: Component,
8806
+ args: [{ selector: 'tabs-widget', template: `
8807
+ <ul
8808
+ [class]="options?.labelHtmlClass || ''">
8809
+ <li *ngFor="let item of layoutNode()?.items; let i = index"
8810
+ [class]="(options?.itemLabelHtmlClass || '') + (selectedItem === i ?
8811
+ (' ' + (options?.activeClass || '') + ' ' + (options?.style?.selected || '')) :
8812
+ (' ' + options?.style?.unselected))"
8813
+ role="presentation"
8814
+ data-tabs>
8815
+ <a *ngIf="showAddTab || item.type !== '$ref'"
8816
+ [class]="'nav-link' + (selectedItem === i ? (' ' + options?.activeClass + ' ' + options?.style?.selected) :
8817
+ (' ' + options?.style?.unselected))"
8818
+ (click)="select(i)">
8819
+ <input type="radio" [value]="i" *ngIf="options?.tabMode=='oneOfMode'"
8820
+ name="tabSelection"
8821
+ [(ngModel)]="selectedItem"
8822
+ [class]="(options?.widget_radioClass || '')"
8823
+ [value]="i"
8824
+ (change)="select(i)"
8825
+ />
8826
+ {{setTabTitle(item, i)}}
8827
+ </a>
8828
+ </li>
8829
+ </ul>
8830
+
8831
+ <div *ngFor="let layoutItem of layoutNode()?.items; let i = index"
8832
+ [class]="(options?.htmlClass || '') + (selectedItem != i?' ngf-hidden':'') ">
8833
+ <!--for now the only difference between oneOfMode and the default
8834
+ is that oneOfMode uses the *ngIf="selectedItem === i" clause, which automatically
8835
+ destroys the tabs that are not rendered while default mode only hide them
8836
+ the upshot is that only the active tabs value will be used
8837
+ -->
8838
+ <ng-container *ngIf="options?.tabMode=='oneOfMode'">
8839
+ <select-framework-widget *ngIf="selectedItem === i"
8840
+ [class]="(options?.fieldHtmlClass || '') +
8841
+ ' ' + (options?.activeClass || '') +
8842
+ ' ' + (options?.style?.selected || '')"
8843
+ [dataIndex]="layoutNode()?.dataType === 'array' ? (dataIndex() || []).concat(i) : dataIndex()"
8844
+ [layoutIndex]="(layoutIndex() || []).concat(i)"
8845
+ [layoutNode]="layoutItem"></select-framework-widget>
8846
+ </ng-container>
8847
+ <ng-container *ngIf="options?.tabMode !='oneOfMode'">
8848
+ <select-framework-widget
8849
+ [class]="(options?.fieldHtmlClass || '') +
8850
+ ' ' + (options?.activeClass || '') +
8851
+ ' ' + (options?.style?.selected || '')"
8852
+ [dataIndex]="layoutNode()?.dataType === 'array' ? (dataIndex() || []).concat(i) : dataIndex()"
8853
+ [layoutIndex]="(layoutIndex() || []).concat(i)"
8854
+ [layoutNode]="layoutItem"></select-framework-widget>
8855
+ </ng-container>
8856
+ </div>`, standalone: false, styles: ["a{cursor:pointer}.ngf-hidden{display:none}\n"] }]
8857
+ }] });
8858
+
7915
8859
  // TODO: Add this control
7916
8860
  class OneOfComponent {
7917
8861
  constructor() {
@@ -7924,20 +8868,101 @@ class OneOfComponent {
7924
8868
  }
7925
8869
  ngOnInit() {
7926
8870
  this.options = this.layoutNode().options || {};
8871
+ this.options.tabMode = "oneOfMode";
8872
+ this.options.selectedTab = this.findSelectedTab();
8873
+ //this.options.description=this.options.description||"choose one of";
7927
8874
  this.jsf.initializeControl(this);
7928
8875
  }
8876
+ findSelectedTab() {
8877
+ //TODO test- this.jsf.formValues seems to be the initial data supplied to the form
8878
+ //while the jsf.formGroup value is derived from the actual controls
8879
+ //let formValue=this.jsf.getFormControlValue(this);
8880
+ let foundInd = -1;
8881
+ //seach for non null value
8882
+ if (this.layoutNode().items) {
8883
+ this.layoutNode().items.forEach((layoutItem, ind) => {
8884
+ let formValue = JsonPointer.get(this.jsf.formValues, layoutItem.dataPointer);
8885
+ if (layoutItem.oneOfPointer) {
8886
+ let controlKey = path2ControlKey(layoutItem.oneOfPointer);
8887
+ let fname = layoutItem.name;
8888
+ if (hasOwn(this.jsf.formGroup.controls, controlKey) &&
8889
+ (formValue || hasNonNullValue(this.jsf.formGroup.controls[controlKey].value))
8890
+ //hasOwn(formValue,fname) && hasOwn(this.jsf.formGroup.controls,controlKey)
8891
+ // && (formValue[fname] || this.jsf.formGroup.controls[controlKey].value)
8892
+ //&&isEqual(formValue[fname],this.jsf.formGroup.controls[controlKey].value)
8893
+ ) {
8894
+ foundInd = ind;
8895
+ }
8896
+ //foundInd=formValue[controlKey]!=null?ind:foundInd;
8897
+ //if no exact match found, then search in descendant values
8898
+ //to see which one of item matches
8899
+ if (foundInd == -1) {
8900
+ //find all descendant oneof paths
8901
+ let descendantOneOfControlNames = Object.keys(this.jsf.formGroup.controls).filter(controlName => {
8902
+ return controlName.startsWith(controlKey);
8903
+ });
8904
+ descendantOneOfControlNames.forEach(controlName => {
8905
+ let parts = controlName.split('$');
8906
+ let fieldName = parts[parts.length - 1];
8907
+ let controlValue = this.jsf.formGroup.controls[controlName].value;
8908
+ let controlSchema = JsonPointer.get(this.jsf.schema, parts.join("/"));
8909
+ let schemaPointer = parts.join("/");
8910
+ let dPointer = schemaPointer.replace(/(anyOf|allOf|oneOf|none)\/[\d]+\//g, '')
8911
+ .replace(/(if|then|else|properties)\//g, '');
8912
+ //JsonPointer.toDataPointer(parts.join("/"),this.jsf.schema);
8913
+ let dVal = JsonPointer.get(this.jsf.formValues, dPointer);
8914
+ let compareVal = dVal; //formValue;
8915
+ //compare only values that are in the subschema properties
8916
+ if (controlSchema && controlSchema.properties) {
8917
+ compareVal = isObject$1(dVal) && hasOwn(dVal, fieldName) ?
8918
+ pick(dVal[fieldName], Object.keys(controlSchema.properties))
8919
+ : pick(dVal, Object.keys(controlSchema.properties));
8920
+ }
8921
+ /*
8922
+ if(isObject(compareVal) && hasOwn(compareVal,fieldName) &&
8923
+ isEqual(compareVal[fieldName],controlValue)
8924
+ ){
8925
+ foundInd=ind;
8926
+ }else //if(formValue || controlValue){
8927
+ if(isEqual(compareVal,controlValue)){
8928
+ foundInd=ind;
8929
+ }
8930
+ */
8931
+ if (isEqual$2(compareVal, controlValue)) {
8932
+ foundInd = ind;
8933
+ }
8934
+ });
8935
+ //now need to compare values
8936
+ }
8937
+ }
8938
+ });
8939
+ }
8940
+ return Math.max(foundInd, 0);
8941
+ }
7929
8942
  updateValue(event) {
7930
8943
  this.jsf.updateValue(this, event.target.value);
7931
8944
  }
8945
+ ngOnDestroy() {
8946
+ //this.jsf.updateValue(this, null);
8947
+ }
7932
8948
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: OneOfComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7933
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: OneOfComponent, selector: "one-of-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: ``, isInline: true }); }
8949
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: OneOfComponent, selector: "one-of-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `<h4>{{this.options?.description}}</h4>
8950
+ <tabs-widget #tabs [layoutNode]="layoutNode()"
8951
+ [layoutIndex]="layoutIndex()"
8952
+ [dataIndex]="dataIndex()" >
8953
+ </tabs-widget>`, isInline: true, dependencies: [{ kind: "component", type: TabsComponent, selector: "tabs-widget", inputs: ["layoutNode", "layoutIndex", "dataIndex"] }] }); }
7934
8954
  }
7935
8955
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: OneOfComponent, decorators: [{
7936
8956
  type: Component,
7937
8957
  args: [{
7938
8958
  // tslint:disable-next-line:component-selector
7939
8959
  selector: 'one-of-widget',
7940
- template: ``,
8960
+ template: `<h4>{{this.options?.description}}</h4>
8961
+ <tabs-widget #tabs [layoutNode]="layoutNode()"
8962
+ [layoutIndex]="layoutIndex()"
8963
+ [dataIndex]="dataIndex()" >
8964
+ </tabs-widget>`,
8965
+ standalone: false
7941
8966
  }]
7942
8967
  }] });
7943
8968
 
@@ -7965,6 +8990,9 @@ class RadiosComponent {
7965
8990
  updateValue(event) {
7966
8991
  this.jsf.updateValue(this, event.target.value);
7967
8992
  }
8993
+ ngOnDestroy() {
8994
+ this.jsf.updateValue(this, null);
8995
+ }
7968
8996
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: RadiosComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7969
8997
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: RadiosComponent, selector: "radios-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
7970
8998
  <label *ngIf="options?.title"
@@ -8087,46 +9115,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
8087
9115
  }]
8088
9116
  }] });
8089
9117
 
8090
- class SelectFrameworkComponent {
8091
- constructor() {
8092
- this.jsf = inject(JsonSchemaFormService);
8093
- this.newComponent = null;
8094
- this.layoutNode = input(undefined);
8095
- this.layoutIndex = input(undefined);
8096
- this.dataIndex = input(undefined);
8097
- this.widgetContainer = viewChild('widgetContainer', { read: ViewContainerRef });
8098
- }
8099
- ngOnInit() {
8100
- this.updateComponent();
8101
- }
8102
- ngOnChanges() {
8103
- this.updateComponent();
8104
- }
8105
- updateComponent() {
8106
- const widgetContainer = this.widgetContainer();
8107
- if (widgetContainer && !this.newComponent && this.jsf.framework) {
8108
- this.newComponent = widgetContainer.createComponent((this.jsf.framework));
8109
- //TODO fix all deprecated calls and test
8110
- //this.widgetContainer.createComponent<any>(this.jsf.framework)
8111
- }
8112
- if (this.newComponent) {
8113
- for (const input of ['layoutNode', 'layoutIndex', 'dataIndex']) {
8114
- this.newComponent.instance[input] = this[input];
8115
- }
8116
- }
8117
- }
8118
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SelectFrameworkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8119
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "17.3.12", type: SelectFrameworkComponent, selector: "select-framework-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "widgetContainer", first: true, predicate: ["widgetContainer"], descendants: true, read: ViewContainerRef, isSignal: true }], usesOnChanges: true, ngImport: i0, template: `<div #widgetContainer></div>`, isInline: true }); }
8120
- }
8121
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SelectFrameworkComponent, decorators: [{
8122
- type: Component,
8123
- args: [{
8124
- // tslint:disable-next-line:component-selector
8125
- selector: 'select-framework-widget',
8126
- template: `<div #widgetContainer></div>`,
8127
- }]
8128
- }] });
8129
-
8130
9118
  /**
8131
9119
  * OrderableDirective
8132
9120
  *
@@ -8282,11 +9270,74 @@ class RootComponent {
8282
9270
  };
8283
9271
  //must set moveLayout to false as nxtSortable already moves it
8284
9272
  this.jsf.moveArrayItem(itemCtx, evt.oldIndex, evt.newIndex, false);
9273
+ },
9274
+ onMove: function (/**Event*/ evt, /**Event*/ originalEvent) {
9275
+ if (evt.related.classList.contains("sortable-fixed")) {
9276
+ //console.log(evt.related);
9277
+ return false;
9278
+ }
8285
9279
  }
8286
9280
  };
8287
9281
  }
8288
9282
  sortableInit(sortable) {
8289
9283
  this.sortableObj = sortable;
9284
+ ///NB issue caused by sortablejs when it its destroyed
9285
+ //this mainly affects checkboxes coupled with conditions
9286
+ //-the value is rechecked
9287
+ //-see https://github.com/SortableJS/Sortable/issues/1052#issuecomment-369613072
9288
+ /* attempt to monkey patch sortable js
9289
+ const originalMethod = sortable._nulling;
9290
+ let zone=this.zone;
9291
+ sortable._nulling=function() {
9292
+ console.log(`pluginEvent 2 ${pluginEvent}`)
9293
+ zone.runOutsideAngular(() => {
9294
+ console.log(`pluginEvent3 ${pluginEvent}`)
9295
+ pluginEvent('nulling', this);
9296
+
9297
+ rootEl =
9298
+ dragEl =
9299
+ parentEl =
9300
+ ghostEl =
9301
+ nextEl =
9302
+ cloneEl =
9303
+ lastDownEl =
9304
+ cloneHidden =
9305
+
9306
+ tapEvt =
9307
+ touchEvt =
9308
+
9309
+ moved =
9310
+ newIndex =
9311
+ newDraggableIndex =
9312
+ oldIndex =
9313
+ oldDraggableIndex =
9314
+
9315
+ lastTarget =
9316
+ lastDirection =
9317
+
9318
+ putSortable =
9319
+ activeGroup =
9320
+ Sortable.dragged =
9321
+ Sortable.ghost =
9322
+ Sortable.clone =
9323
+ Sortable.active = null;
9324
+
9325
+
9326
+ let el = this.el;
9327
+ savedInputChecked.forEach(function (checkEl) {
9328
+ if (el.contains(checkEl)) {
9329
+ checkEl.checked = true;
9330
+ }
9331
+ });
9332
+
9333
+ savedInputChecked.length =
9334
+ lastDx =
9335
+ lastDy = 0;
9336
+
9337
+ })
9338
+
9339
+ }.bind(sortable)
9340
+ */
8290
9341
  }
8291
9342
  isDraggable(node) {
8292
9343
  let result = node.arrayItem && node.type !== '$ref' &&
@@ -8298,6 +9349,12 @@ class RootComponent {
8298
9349
  }
8299
9350
  return result;
8300
9351
  }
9352
+ //TODO also need to think of other types such as button which can be
9353
+ //created by an arbitrary layout
9354
+ isFixed(node) {
9355
+ let result = node.type == '$ref';
9356
+ return result;
9357
+ }
8301
9358
  // Set attributes for flexbox child
8302
9359
  // (container attributes are set in section.component)
8303
9360
  getFlexAttribute(node, attribute) {
@@ -8337,6 +9394,7 @@ class RootComponent {
8337
9394
  [style.flex-shrink]="getFlexAttribute(layoutItem, 'flex-shrink')"
8338
9395
  [style.order]="(layoutItem.options || {}).order"
8339
9396
  [class.sortable-filter]="!isDraggable(layoutItem)"
9397
+ [class.sortable-fixed]="isFixed(layoutItem)"
8340
9398
  >
8341
9399
  <!--NB orderable directive is not used but has been left in for now and set to false
8342
9400
  otherwise the compiler won't recognize dataIndex and other dependent attributes
@@ -8347,6 +9405,7 @@ class RootComponent {
8347
9405
  [layoutNode]="layoutItem"
8348
9406
  [orderable]="false"
8349
9407
  [class.sortable-filter]="!isDraggable(layoutItem)"
9408
+ [class.sortable-fixed]="isFixed(layoutItem)"
8350
9409
  >
8351
9410
  <select-framework-widget *ngIf="showWidget(layoutItem)"
8352
9411
  [dataIndex]="layoutItem?.arrayItem ? (dataIndex() || []).concat(i) : (dataIndex() || [])"
@@ -8369,6 +9428,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
8369
9428
  [style.flex-shrink]="getFlexAttribute(layoutItem, 'flex-shrink')"
8370
9429
  [style.order]="(layoutItem.options || {}).order"
8371
9430
  [class.sortable-filter]="!isDraggable(layoutItem)"
9431
+ [class.sortable-fixed]="isFixed(layoutItem)"
8372
9432
  >
8373
9433
  <!--NB orderable directive is not used but has been left in for now and set to false
8374
9434
  otherwise the compiler won't recognize dataIndex and other dependent attributes
@@ -8379,6 +9439,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
8379
9439
  [layoutNode]="layoutItem"
8380
9440
  [orderable]="false"
8381
9441
  [class.sortable-filter]="!isDraggable(layoutItem)"
9442
+ [class.sortable-fixed]="isFixed(layoutItem)"
8382
9443
  >
8383
9444
  <select-framework-widget *ngIf="showWidget(layoutItem)"
8384
9445
  [dataIndex]="layoutItem?.arrayItem ? (dataIndex() || []).concat(i) : (dataIndex() || [])"
@@ -8587,6 +9648,7 @@ class SelectComponent {
8587
9648
  this.controlDisabled = false;
8588
9649
  this.boundControl = false;
8589
9650
  this.selectList = [];
9651
+ this.selectListFlatGroup = [];
8590
9652
  this.isArray = isArray;
8591
9653
  this.layoutNode = input(undefined);
8592
9654
  this.layoutIndex = input(undefined);
@@ -8595,10 +9657,40 @@ class SelectComponent {
8595
9657
  ngOnInit() {
8596
9658
  this.options = this.layoutNode().options || {};
8597
9659
  this.selectList = buildTitleMap(this.options.titleMap || this.options.enumNames, this.options.enum, !!this.options.required, !!this.options.flatList);
9660
+ //the selectListFlatGroup array will be used to update the formArray values
9661
+ //while the selectList array will be bound to the form select
9662
+ //as either a grouped select or a flat select
9663
+ this.selectListFlatGroup = buildTitleMap(this.options.titleMap || this.options.enumNames, this.options.enum, !!this.options.required, true);
8598
9664
  this.jsf.initializeControl(this);
8599
9665
  }
9666
+ deselectAll() {
9667
+ this.selectListFlatGroup.forEach(selItem => {
9668
+ selItem.checked = false;
9669
+ });
9670
+ }
8600
9671
  updateValue(event) {
8601
- this.jsf.updateValue(this, event.target.value);
9672
+ this.options.showErrors = true;
9673
+ if (this.options.multiple) {
9674
+ if (this.controlValue?.includes(null)) {
9675
+ this.deselectAll();
9676
+ //this.control.setValue([]); // Reset the form control to an empty array
9677
+ //this.selectList=JSON.parse(JSON.stringify(this.selectList));
9678
+ this.jsf.updateArrayMultiSelectList(this, []);
9679
+ }
9680
+ else {
9681
+ this.selectListFlatGroup.forEach(selItem => {
9682
+ selItem.checked = this.controlValue?.indexOf(selItem.value) >= 0 ? true : false;
9683
+ });
9684
+ this.jsf.updateArrayMultiSelectList(this, this.selectListFlatGroup);
9685
+ }
9686
+ return;
9687
+ }
9688
+ this.jsf.updateValue(this, this.controlValue);
9689
+ }
9690
+ ngOnDestroy() {
9691
+ let nullVal = this.options.multiple ? [null] : null;
9692
+ this.formControl.reset(nullVal);
9693
+ this.controlValue = null;
8602
9694
  }
8603
9695
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8604
9696
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: SelectComponent, selector: "select-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
@@ -8609,7 +9701,7 @@ class SelectComponent {
8609
9701
  [class]="options?.labelHtmlClass || ''"
8610
9702
  [style.display]="options?.notitle ? 'none' : ''"
8611
9703
  [innerHTML]="options?.title"></label>
8612
- <select *ngIf="boundControl"
9704
+ <select *ngIf="boundControl && !options?.multiple"
8613
9705
  [formControl]="formControl"
8614
9706
  [attr.aria-describedby]="'control' + layoutNode()?._id + 'Status'"
8615
9707
  [attr.readonly]="options?.readonly ? 'readonly' : null"
@@ -8656,7 +9748,34 @@ class SelectComponent {
8656
9748
  </optgroup>
8657
9749
  </ng-template>
8658
9750
  </select>
8659
- </div>`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] }); }
9751
+ <select *ngIf="boundControl && options?.multiple"
9752
+ [attr.aria-describedby]="'control' + layoutNode()?._id + 'Status'"
9753
+ [attr.readonly]="options?.readonly ? 'readonly' : null"
9754
+ [attr.required]="options?.required"
9755
+ [class]="options?.fieldHtmlClass || ''"
9756
+ [disabled]="controlDisabled"
9757
+ [id]="'control' + layoutNode()?._id"
9758
+ [multiple]="options?.multiple"
9759
+ [name]="controlName"
9760
+ [(ngModel)]="controlValue"
9761
+ (change)="updateValue($event)">
9762
+ <ng-template ngFor let-selectItem [ngForOf]="selectList">
9763
+ <option *ngIf="!isArray(selectItem?.items)"
9764
+ [selected]="selectItem?.value === controlValue"
9765
+ [value]="selectItem?.value">
9766
+ <span [innerHTML]="selectItem?.name"></span>
9767
+ </option>
9768
+ <optgroup *ngIf="isArray(selectItem?.items)"
9769
+ [label]="selectItem?.group">
9770
+ <option *ngFor="let subItem of selectItem.items"
9771
+ [attr.selected]="subItem?.value === controlValue"
9772
+ [value]="subItem?.value">
9773
+ <span [innerHTML]="subItem?.name"></span>
9774
+ </option>
9775
+ </optgroup>
9776
+ </ng-template>
9777
+ </select>
9778
+ </div>`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.SelectMultipleControlValueAccessor, selector: "select[multiple][formControlName],select[multiple][formControl],select[multiple][ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] }); }
8660
9779
  }
8661
9780
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SelectComponent, decorators: [{
8662
9781
  type: Component,
@@ -8671,7 +9790,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
8671
9790
  [class]="options?.labelHtmlClass || ''"
8672
9791
  [style.display]="options?.notitle ? 'none' : ''"
8673
9792
  [innerHTML]="options?.title"></label>
8674
- <select *ngIf="boundControl"
9793
+ <select *ngIf="boundControl && !options?.multiple"
8675
9794
  [formControl]="formControl"
8676
9795
  [attr.aria-describedby]="'control' + layoutNode()?._id + 'Status'"
8677
9796
  [attr.readonly]="options?.readonly ? 'readonly' : null"
@@ -8718,6 +9837,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
8718
9837
  </optgroup>
8719
9838
  </ng-template>
8720
9839
  </select>
9840
+ <select *ngIf="boundControl && options?.multiple"
9841
+ [attr.aria-describedby]="'control' + layoutNode()?._id + 'Status'"
9842
+ [attr.readonly]="options?.readonly ? 'readonly' : null"
9843
+ [attr.required]="options?.required"
9844
+ [class]="options?.fieldHtmlClass || ''"
9845
+ [disabled]="controlDisabled"
9846
+ [id]="'control' + layoutNode()?._id"
9847
+ [multiple]="options?.multiple"
9848
+ [name]="controlName"
9849
+ [(ngModel)]="controlValue"
9850
+ (change)="updateValue($event)">
9851
+ <ng-template ngFor let-selectItem [ngForOf]="selectList">
9852
+ <option *ngIf="!isArray(selectItem?.items)"
9853
+ [selected]="selectItem?.value === controlValue"
9854
+ [value]="selectItem?.value">
9855
+ <span [innerHTML]="selectItem?.name"></span>
9856
+ </option>
9857
+ <optgroup *ngIf="isArray(selectItem?.items)"
9858
+ [label]="selectItem?.group">
9859
+ <option *ngFor="let subItem of selectItem.items"
9860
+ [attr.selected]="subItem?.value === controlValue"
9861
+ [value]="subItem?.value">
9862
+ <span [innerHTML]="subItem?.name"></span>
9863
+ </option>
9864
+ </optgroup>
9865
+ </ng-template>
9866
+ </select>
8721
9867
  </div>`,
8722
9868
  }]
8723
9869
  }] });
@@ -8734,6 +9880,7 @@ class SubmitComponent {
8734
9880
  ngOnDestroy() {
8735
9881
  this.isValidChangesSubs?.unsubscribe();
8736
9882
  this.isValidChangesSubs = null;
9883
+ this.updateValue({ target: { value: null } });
8737
9884
  }
8738
9885
  ngOnInit() {
8739
9886
  this.options = this.layoutNode().options || {};
@@ -8831,107 +9978,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
8831
9978
  }]
8832
9979
  }] });
8833
9980
 
8834
- class TabsComponent {
8835
- constructor() {
8836
- this.jsf = inject(JsonSchemaFormService);
8837
- this.selectedItem = 0;
8838
- this.showAddTab = true;
8839
- this.layoutNode = input(undefined);
8840
- this.layoutIndex = input(undefined);
8841
- this.dataIndex = input(undefined);
8842
- }
8843
- ngOnInit() {
8844
- this.options = this.layoutNode().options || {};
8845
- this.itemCount = this.layoutNode().items.length - 1;
8846
- this.updateControl();
8847
- }
8848
- select(index) {
8849
- const layoutNode = this.layoutNode();
8850
- if (layoutNode.items[index].type === '$ref') {
8851
- this.itemCount = layoutNode.items.length;
8852
- this.jsf.addItem({
8853
- layoutNode: signal(layoutNode.items[index]),
8854
- layoutIndex: signal(this.layoutIndex().concat(index)),
8855
- dataIndex: signal(this.dataIndex().concat(index))
8856
- });
8857
- this.updateControl();
8858
- }
8859
- this.selectedItem = index;
8860
- }
8861
- updateControl() {
8862
- const lastItem = this.layoutNode().items[this.layoutNode().items.length - 1];
8863
- if (lastItem.type === '$ref' &&
8864
- this.itemCount >= (lastItem.options.maxItems || 1000)) {
8865
- this.showAddTab = false;
8866
- }
8867
- }
8868
- setTabTitle(item, index) {
8869
- return this.jsf.setArrayItemTitle(this, item, index);
8870
- }
8871
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8872
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: TabsComponent, selector: "tabs-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
8873
- <ul
8874
- [class]="options?.labelHtmlClass || ''">
8875
- <li *ngFor="let item of layoutNode()?.items; let i = index"
8876
- [class]="(options?.itemLabelHtmlClass || '') + (selectedItem === i ?
8877
- (' ' + (options?.activeClass || '') + ' ' + (options?.style?.selected || '')) :
8878
- (' ' + options?.style?.unselected))"
8879
- role="presentation"
8880
- data-tabs>
8881
- <a *ngIf="showAddTab || item.type !== '$ref'"
8882
- [class]="'nav-link' + (selectedItem === i ? (' ' + options?.activeClass + ' ' + options?.style?.selected) :
8883
- (' ' + options?.style?.unselected))"
8884
- [innerHTML]="setTabTitle(item, i)"
8885
- (click)="select(i)"></a>
8886
- </li>
8887
- </ul>
8888
-
8889
- <div *ngFor="let layoutItem of layoutNode()?.items; let i = index"
8890
- [class]="options?.htmlClass || ''">
8891
-
8892
- <select-framework-widget *ngIf="selectedItem === i"
8893
- [class]="(options?.fieldHtmlClass || '') +
8894
- ' ' + (options?.activeClass || '') +
8895
- ' ' + (options?.style?.selected || '')"
8896
- [dataIndex]="layoutNode()?.dataType === 'array' ? (dataIndex() || []).concat(i) : dataIndex()"
8897
- [layoutIndex]="(layoutIndex() || []).concat(i)"
8898
- [layoutNode]="layoutItem"></select-framework-widget>
8899
-
8900
- </div>`, isInline: true, styles: ["a{cursor:pointer}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: SelectFrameworkComponent, selector: "select-framework-widget", inputs: ["layoutNode", "layoutIndex", "dataIndex"] }] }); }
8901
- }
8902
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TabsComponent, decorators: [{
8903
- type: Component,
8904
- args: [{ selector: 'tabs-widget', template: `
8905
- <ul
8906
- [class]="options?.labelHtmlClass || ''">
8907
- <li *ngFor="let item of layoutNode()?.items; let i = index"
8908
- [class]="(options?.itemLabelHtmlClass || '') + (selectedItem === i ?
8909
- (' ' + (options?.activeClass || '') + ' ' + (options?.style?.selected || '')) :
8910
- (' ' + options?.style?.unselected))"
8911
- role="presentation"
8912
- data-tabs>
8913
- <a *ngIf="showAddTab || item.type !== '$ref'"
8914
- [class]="'nav-link' + (selectedItem === i ? (' ' + options?.activeClass + ' ' + options?.style?.selected) :
8915
- (' ' + options?.style?.unselected))"
8916
- [innerHTML]="setTabTitle(item, i)"
8917
- (click)="select(i)"></a>
8918
- </li>
8919
- </ul>
8920
-
8921
- <div *ngFor="let layoutItem of layoutNode()?.items; let i = index"
8922
- [class]="options?.htmlClass || ''">
8923
-
8924
- <select-framework-widget *ngIf="selectedItem === i"
8925
- [class]="(options?.fieldHtmlClass || '') +
8926
- ' ' + (options?.activeClass || '') +
8927
- ' ' + (options?.style?.selected || '')"
8928
- [dataIndex]="layoutNode()?.dataType === 'array' ? (dataIndex() || []).concat(i) : dataIndex()"
8929
- [layoutIndex]="(layoutIndex() || []).concat(i)"
8930
- [layoutNode]="layoutItem"></select-framework-widget>
8931
-
8932
- </div>`, styles: ["a{cursor:pointer}\n"] }]
8933
- }] });
8934
-
8935
9981
  class TemplateComponent {
8936
9982
  constructor() {
8937
9983
  this.jsf = inject(JsonSchemaFormService);
@@ -8987,6 +10033,9 @@ class TextareaComponent {
8987
10033
  updateValue(event) {
8988
10034
  this.jsf.updateValue(this, event.target.value);
8989
10035
  }
10036
+ ngOnDestroy() {
10037
+ this.jsf.updateValue(this, null);
10038
+ }
8990
10039
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TextareaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8991
10040
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: TextareaComponent, selector: "textarea-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
8992
10041
  <div
@@ -9287,7 +10336,13 @@ class WidgetLibraryModule {
9287
10336
  //disabled:false,
9288
10337
  //draggable:".draggableitem",//">:not(.nonsort)",//">.draggable-item",//":not(.nonsort)",//">*",//":not(.nonsort)",//":not(.non-draggable)",
9289
10338
  filter: ".sortable-filter",
9290
- preventOnFilter: false //needed for input range elements slider do still work
10339
+ preventOnFilter: false,
10340
+ onMove: function (/**Event*/ evt, /**Event*/ originalEvent) {
10341
+ if (evt.related.classList.contains("sortable-fixed")) {
10342
+ //console.log(evt.related);
10343
+ return false;
10344
+ }
10345
+ }
9291
10346
  })] }); }
9292
10347
  }
9293
10348
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: WidgetLibraryModule, decorators: [{
@@ -9298,7 +10353,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
9298
10353
  //disabled:false,
9299
10354
  //draggable:".draggableitem",//">:not(.nonsort)",//">.draggable-item",//":not(.nonsort)",//">*",//":not(.nonsort)",//":not(.non-draggable)",
9300
10355
  filter: ".sortable-filter",
9301
- preventOnFilter: false //needed for input range elements slider do still work
10356
+ preventOnFilter: false,
10357
+ onMove: function (/**Event*/ evt, /**Event*/ originalEvent) {
10358
+ if (evt.related.classList.contains("sortable-fixed")) {
10359
+ //console.log(evt.related);
10360
+ return false;
10361
+ }
10362
+ }
9302
10363
  })],
9303
10364
  declarations: [...BASIC_WIDGETS, OrderableDirective, ElementAttributeDirective],
9304
10365
  exports: [...BASIC_WIDGETS, OrderableDirective, ElementAttributeDirective]
@@ -9543,7 +10604,7 @@ class JsonSchemaFormComponent {
9543
10604
  this.previousInputs = {
9544
10605
  schema: null, layout: null, data: null, options: null, framework: null,
9545
10606
  widgets: null, form: null, model: null, JSONSchema: null, UISchema: null,
9546
- formData: null, loadExternalAssets: null, debug: null,
10607
+ formData: null, loadExternalAssets: null, debug: null, ajvOptions: null
9547
10608
  };
9548
10609
  // Recommended inputs
9549
10610
  this.schema = input(undefined); // The JSON Schema
@@ -9566,6 +10627,7 @@ class JsonSchemaFormComponent {
9566
10627
  this.loadExternalAssets = input(undefined); // Load external framework assets?
9567
10628
  this.debug = input(undefined); // Show debug information?
9568
10629
  this.theme = input(undefined); // Theme
10630
+ this.ajvOptions = input(undefined); // ajvOptions
9569
10631
  // Outputs
9570
10632
  this.onChanges = output(); // Live unvalidated internal form data
9571
10633
  this.onSubmit = output(); // Complete validated form data
@@ -9787,6 +10849,7 @@ class JsonSchemaFormComponent {
9787
10849
  this.jsf.data) {
9788
10850
  // Reset all form values to defaults
9789
10851
  this.jsf.resetAllValues();
10852
+ this.initializeAjv();
9790
10853
  this.initializeOptions(); // Update options
9791
10854
  this.initializeSchema(); // Update schema, schemaRefLibrary,
9792
10855
  // schemaRecursiveRefMap, & dataRecursiveRefMap
@@ -9837,6 +10900,20 @@ class JsonSchemaFormComponent {
9837
10900
  this.formInitialized = true;
9838
10901
  }
9839
10902
  }
10903
+ /**
10904
+ * 'initializeAjv' function
10905
+ *
10906
+ * Initialize ajv from 'ajvOptions'
10907
+ */
10908
+ initializeAjv() {
10909
+ const form = this.form();
10910
+ const ajvOptions = cloneDeep(this.ajvOptions()) ||
10911
+ (form && hasOwn(form, 'ajvOptions') && isObject(form.ajvOptions)
10912
+ && cloneDeep(form.ajvOptions));
10913
+ if (ajvOptions) {
10914
+ this.ajvInstanceName = this.jsf.createAndRegisterAjvInstance(ajvOptions).name;
10915
+ }
10916
+ }
9840
10917
  /**
9841
10918
  * 'initializeOptions' function
9842
10919
  *
@@ -9960,7 +11037,8 @@ class JsonSchemaFormComponent {
9960
11037
  // draft 3 (JSON Form style) and draft 4 (Angular Schema Form style)
9961
11038
  this.jsf.schema = convertSchemaToDraft6(this.jsf.schema);
9962
11039
  // Initialize ajv and compile schema
9963
- this.jsf.compileAjvSchema();
11040
+ //this.jsf.compileAjvSchema();
11041
+ //moved to initializeAjv()
9964
11042
  // Create schemaRefLibrary, schemaRecursiveRefMap, dataRecursiveRefMap, & arrayMap
9965
11043
  this.jsf.schema = resolveSchemaReferences(this.jsf.schema, this.jsf.schemaRefLibrary, this.jsf.schemaRecursiveRefMap, this.jsf.dataRecursiveRefMap, this.jsf.arrayMap);
9966
11044
  if (hasOwn(this.jsf.schemaRefLibrary, '')) {
@@ -10164,7 +11242,8 @@ class JsonSchemaFormComponent {
10164
11242
  }
10165
11243
  if (!isEmpty(this.jsf.schema)) {
10166
11244
  // If not already initialized, initialize ajv and compile schema
10167
- this.jsf.compileAjvSchema();
11245
+ //this.jsf.compileAjvSchema();
11246
+ //moved to initializeAjv()
10168
11247
  // Update all layout elements, add values, widgets, and validators,
10169
11248
  // replace any '*' with a layout built from all schema elements,
10170
11249
  // and update the FormGroup template with any new validators
@@ -10172,7 +11251,7 @@ class JsonSchemaFormComponent {
10172
11251
  // Build the Angular FormGroup template from the schema
10173
11252
  this.jsf.buildFormGroupTemplate(this.jsf.formValues);
10174
11253
  // Build the real Angular FormGroup from the FormGroup template
10175
- this.jsf.buildFormGroup();
11254
+ this.jsf.buildFormGroup(this.ajvInstanceName);
10176
11255
  }
10177
11256
  if (this.jsf.formGroup) {
10178
11257
  // Reset initial form values
@@ -10223,7 +11302,7 @@ class JsonSchemaFormComponent {
10223
11302
  }
10224
11303
  }
10225
11304
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: JsonSchemaFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
10226
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: JsonSchemaFormComponent, selector: "json-schema-form", inputs: { schema: { classPropertyName: "schema", publicName: "schema", isSignal: true, isRequired: false, transformFunction: null }, layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, framework: { classPropertyName: "framework", publicName: "framework", isSignal: true, isRequired: false, transformFunction: null }, widgets: { classPropertyName: "widgets", publicName: "widgets", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: false, transformFunction: null }, JSONSchema: { classPropertyName: "JSONSchema", publicName: "JSONSchema", isSignal: true, isRequired: false, transformFunction: null }, UISchema: { classPropertyName: "UISchema", publicName: "UISchema", isSignal: true, isRequired: false, transformFunction: null }, formData: { classPropertyName: "formData", publicName: "formData", isSignal: true, isRequired: false, transformFunction: null }, ngModel: { classPropertyName: "ngModel", publicName: "ngModel", isSignal: true, isRequired: false, transformFunction: null }, language: { classPropertyName: "language", publicName: "language", isSignal: true, isRequired: false, transformFunction: null }, loadExternalAssets: { classPropertyName: "loadExternalAssets", publicName: "loadExternalAssets", isSignal: true, isRequired: false, transformFunction: null }, debug: { classPropertyName: "debug", publicName: "debug", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { onChanges: "onChanges", onSubmit: "onSubmit", isValid: "isValid", validationErrors: "validationErrors", formSchema: "formSchema", formLayout: "formLayout", dataChange: "dataChange", modelChange: "modelChange", formDataChange: "formDataChange", ngModelChange: "ngModelChange" }, providers: [JsonSchemaFormService, JSON_SCHEMA_FORM_VALUE_ACCESSOR], usesOnChanges: true, ngImport: i0, template: "<form [autocomplete]=\"jsf?.formOptions?.autocomplete ? 'on' : 'off'\" class=\"json-schema-form\" (ngSubmit)=\"submitForm()\">\r\n <root-widget [layout]=\"jsf?.layout\"></root-widget>\r\n</form>\r\n<div *ngIf=\"debug() || jsf?.formOptions?.debug\">\r\n Debug output:\r\n <pre>{{debugOutput}}</pre>\r\n</div>", dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: RootComponent, selector: "root-widget", inputs: ["dataIndex", "layoutIndex", "layout", "isOrderable", "isFlexItem"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11305
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: JsonSchemaFormComponent, selector: "json-schema-form", inputs: { schema: { classPropertyName: "schema", publicName: "schema", isSignal: true, isRequired: false, transformFunction: null }, layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, framework: { classPropertyName: "framework", publicName: "framework", isSignal: true, isRequired: false, transformFunction: null }, widgets: { classPropertyName: "widgets", publicName: "widgets", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: false, transformFunction: null }, JSONSchema: { classPropertyName: "JSONSchema", publicName: "JSONSchema", isSignal: true, isRequired: false, transformFunction: null }, UISchema: { classPropertyName: "UISchema", publicName: "UISchema", isSignal: true, isRequired: false, transformFunction: null }, formData: { classPropertyName: "formData", publicName: "formData", isSignal: true, isRequired: false, transformFunction: null }, ngModel: { classPropertyName: "ngModel", publicName: "ngModel", isSignal: true, isRequired: false, transformFunction: null }, language: { classPropertyName: "language", publicName: "language", isSignal: true, isRequired: false, transformFunction: null }, loadExternalAssets: { classPropertyName: "loadExternalAssets", publicName: "loadExternalAssets", isSignal: true, isRequired: false, transformFunction: null }, debug: { classPropertyName: "debug", publicName: "debug", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null }, ajvOptions: { classPropertyName: "ajvOptions", publicName: "ajvOptions", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { onChanges: "onChanges", onSubmit: "onSubmit", isValid: "isValid", validationErrors: "validationErrors", formSchema: "formSchema", formLayout: "formLayout", dataChange: "dataChange", modelChange: "modelChange", formDataChange: "formDataChange", ngModelChange: "ngModelChange" }, providers: [JsonSchemaFormService, JSON_SCHEMA_FORM_VALUE_ACCESSOR], usesOnChanges: true, ngImport: i0, template: "<form [autocomplete]=\"jsf?.formOptions?.autocomplete ? 'on' : 'off'\" class=\"json-schema-form\" (ngSubmit)=\"submitForm()\">\r\n <root-widget [layout]=\"jsf?.layout\"></root-widget>\r\n</form>\r\n<div *ngIf=\"debug() || jsf?.formOptions?.debug\">\r\n Debug output:\r\n <pre>{{debugOutput}}</pre>\r\n</div>", dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: RootComponent, selector: "root-widget", inputs: ["dataIndex", "layoutIndex", "layout", "isOrderable", "isFlexItem"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
10227
11306
  }
10228
11307
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: JsonSchemaFormComponent, decorators: [{
10229
11308
  type: Component,
@@ -10259,5 +11338,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
10259
11338
  * Generated bundle index. Do not edit.
10260
11339
  */
10261
11340
 
10262
- export { AddReferenceComponent, BASIC_WIDGETS, ButtonComponent, CheckboxComponent, CheckboxesComponent, ElementAttributeDirective, FileComponent, Framework, FrameworkLibraryService, HiddenComponent, InputComponent, JsonPointer, JsonSchemaFormComponent, JsonSchemaFormModule, JsonSchemaFormService, JsonValidators, MessageComponent, NoneComponent, NumberComponent, OneOfComponent, OrderableDirective, RadiosComponent, RootComponent, SectionComponent, SelectComponent, SelectFrameworkComponent, SelectWidgetComponent, SubmitComponent, TabComponent, TabsComponent, TemplateComponent, TextareaComponent, WidgetLibraryModule, WidgetLibraryService, _executeAsyncValidators, _executeValidators, _mergeErrors, _mergeObjects, _toPromise, addClasses, buildFormGroup, buildFormGroupTemplate, buildLayout, buildLayoutFromSchema, buildSchemaFromData, buildSchemaFromLayout, buildTitleMap, checkInlineType, combineAllOf, commonItems, convertSchemaToDraft6, copy, deValidationMessages, enValidationMessages, esValidationMessages, fixRequiredArrayProperties, fixTitle, forEach, forEachCopy, formatFormData, frValidationMessages, getControl, getControlValidators, getFromSchema, getInputType, getLayoutNode, getSubSchema, getTitleMapFromOneOf, getType, hasOwn, hasValue, inArray, isArray, isBoolean, isDate, isDefined, isEmpty, isFunction, isInputRequired, isInteger, isMap, isNumber, isObject, isObservable, isPrimitive, isPromise, isSet, isString, isType, itValidationMessages, mapLayout, mergeFilteredObject, mergeSchemas, ptValidationMessages, removeRecursiveReferences, resolveSchemaReferences, setRequiredFields, toJavaScriptType, toObservable, toSchemaType, toTitleCase, uniqueItems, updateInputOptions, xor, zhValidationMessages };
11341
+ export { AddReferenceComponent, BASIC_WIDGETS, ButtonComponent, CheckboxComponent, CheckboxesComponent, ElementAttributeDirective, FileComponent, Framework, FrameworkLibraryService, HiddenComponent, InputComponent, JsonPointer, JsonSchemaFormComponent, JsonSchemaFormModule, JsonSchemaFormService, JsonValidators, MessageComponent, NoneComponent, NumberComponent, OneOfComponent, OrderableDirective, RadiosComponent, RootComponent, SectionComponent, SelectComponent, SelectFrameworkComponent, SelectWidgetComponent, SubmitComponent, TabComponent, TabsComponent, TemplateComponent, TextareaComponent, WidgetLibraryModule, WidgetLibraryService, _executeAsyncValidators, _executeValidators, _mergeErrors, _mergeObjects, _toPromise, addClasses, buildFormGroup, buildFormGroupTemplate, buildLayout, buildLayoutFromSchema, buildSchemaFromData, buildSchemaFromLayout, buildTitleMap, checkInlineType, combineAllOf, commonItems, convertSchemaToDraft6, copy, deValidationMessages, enValidationMessages, esValidationMessages, fixRequiredArrayProperties, fixTitle, forEach, forEachCopy, formatFormData, frValidationMessages, getControl, getControlValidators, getFromSchema, getInputType, getLayoutNode, getSubSchema, getTitleMapFromOneOf, getType, hasNonNullValue, hasOwn, hasValue, inArray, isArray, isBoolean, isDate, isDefined, isEmpty, isFunction, isInputRequired, isInteger, isMap, isNumber, isObject, isObservable, isPrimitive, isPromise, isSet, isString, isType, itValidationMessages, mapLayout, mergeFilteredObject, mergeSchemas, path2ControlKey, ptValidationMessages, removeRecursiveReferences, resolveSchemaReferences, setControl, setRequiredFields, toJavaScriptType, toObservable, toSchemaType, toTitleCase, uniqueItems, updateInputOptions, xor, zhValidationMessages };
10263
11342
  //# sourceMappingURL=ng-formworks-core.mjs.map