@ngx-formly/core 7.0.1 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +10 -0
  2. package/fesm2022/ngx-formly-core-json-schema.mjs +259 -39
  3. package/fesm2022/ngx-formly-core-json-schema.mjs.map +1 -1
  4. package/fesm2022/ngx-formly-core-preset.mjs +4 -4
  5. package/fesm2022/ngx-formly-core-preset.mjs.map +1 -1
  6. package/fesm2022/ngx-formly-core-select.mjs +28 -21
  7. package/fesm2022/ngx-formly-core-select.mjs.map +1 -1
  8. package/fesm2022/ngx-formly-core-testing.mjs +3 -3
  9. package/fesm2022/ngx-formly-core-testing.mjs.map +1 -1
  10. package/fesm2022/ngx-formly-core.mjs +621 -78
  11. package/fesm2022/ngx-formly-core.mjs.map +1 -1
  12. package/json-schema/formly-json-schema.service.d.ts +8 -0
  13. package/lib/components/formly.field.d.ts +7 -4
  14. package/lib/core.config.d.ts +1 -0
  15. package/lib/core.d.ts +2 -2
  16. package/lib/core.module.d.ts +7 -0
  17. package/lib/extensions/field-expression/field-expression.d.ts +3 -12
  18. package/lib/extensions/field-expression/utils.d.ts +4 -2
  19. package/lib/extensions/field-expression-legacy/field-expression.d.ts +16 -0
  20. package/lib/extensions/field-expression-legacy/utils.d.ts +5 -0
  21. package/lib/templates/field-array.type.d.ts +1 -1
  22. package/lib/templates/field.type.d.ts +1 -1
  23. package/lib/templates/field.wrapper.d.ts +1 -1
  24. package/lib/templates/formly.attributes.d.ts +5 -2
  25. package/lib/templates/formly.validation-message.d.ts +2 -2
  26. package/package.json +2 -12
  27. package/select/select-options.pipe.d.ts +1 -0
  28. package/esm2022/json-schema/formly-json-schema.service.mjs +0 -628
  29. package/esm2022/json-schema/ngx-formly-core-json-schema.mjs +0 -5
  30. package/esm2022/json-schema/public_api.mjs +0 -2
  31. package/esm2022/lib/components/formly.field.mjs +0 -292
  32. package/esm2022/lib/components/formly.form.mjs +0 -171
  33. package/esm2022/lib/components/formly.template.mjs +0 -36
  34. package/esm2022/lib/core.config.mjs +0 -50
  35. package/esm2022/lib/core.mjs +0 -20
  36. package/esm2022/lib/core.module.mjs +0 -74
  37. package/esm2022/lib/extensions/core/core.mjs +0 -144
  38. package/esm2022/lib/extensions/field-expression/field-expression.mjs +0 -246
  39. package/esm2022/lib/extensions/field-expression/utils.mjs +0 -17
  40. package/esm2022/lib/extensions/field-form/field-form.mjs +0 -141
  41. package/esm2022/lib/extensions/field-form/utils.mjs +0 -100
  42. package/esm2022/lib/extensions/field-validation/field-validation.mjs +0 -135
  43. package/esm2022/lib/extensions/index.mjs +0 -2
  44. package/esm2022/lib/models/config.mjs +0 -2
  45. package/esm2022/lib/models/fieldconfig.cache.mjs +0 -2
  46. package/esm2022/lib/models/fieldconfig.mjs +0 -2
  47. package/esm2022/lib/models/index.mjs +0 -4
  48. package/esm2022/lib/services/formly.builder.mjs +0 -98
  49. package/esm2022/lib/services/formly.config.mjs +0 -236
  50. package/esm2022/lib/templates/field-array.type.mjs +0 -84
  51. package/esm2022/lib/templates/field-template.type.mjs +0 -34
  52. package/esm2022/lib/templates/field.type.mjs +0 -57
  53. package/esm2022/lib/templates/field.wrapper.mjs +0 -21
  54. package/esm2022/lib/templates/formly.attributes.mjs +0 -201
  55. package/esm2022/lib/templates/formly.group.mjs +0 -31
  56. package/esm2022/lib/templates/formly.validation-message.mjs +0 -84
  57. package/esm2022/lib/utils.mjs +0 -312
  58. package/esm2022/ngx-formly-core.mjs +0 -5
  59. package/esm2022/preset/ngx-formly-core-preset.mjs +0 -5
  60. package/esm2022/preset/preset-substitution.extension.mjs +0 -30
  61. package/esm2022/preset/preset.config.mjs +0 -11
  62. package/esm2022/preset/preset.module.mjs +0 -18
  63. package/esm2022/preset/public_api.mjs +0 -3
  64. package/esm2022/public_api.mjs +0 -5
  65. package/esm2022/select/ngx-formly-core-select.mjs +0 -5
  66. package/esm2022/select/public_api.mjs +0 -3
  67. package/esm2022/select/select-options.pipe.mjs +0 -108
  68. package/esm2022/select/select.module.mjs +0 -16
  69. package/esm2022/testing/component-factory.mjs +0 -86
  70. package/esm2022/testing/ngx-formly-core-testing.mjs +0 -5
  71. package/esm2022/testing/public_api.mjs +0 -2
  72. package/lib/extensions/index.d.ts +0 -1
package/README.md CHANGED
@@ -57,6 +57,7 @@ Formly is a dynamic (JSON powered) form library for Angular that brings unmatche
57
57
 
58
58
  | Angular version | Formly version |
59
59
  | --------------- | ---------------------- |
60
+ | Angular >= 19 | `@ngx-formly/core@8.x` |
60
61
  | Angular >= 18 | `@ngx-formly/core@7.x` |
61
62
  | Angular >= 13 | `@ngx-formly/core@6.x` |
62
63
  | Angular >= 7 | `@ngx-formly/core@5.x` |
@@ -65,6 +66,15 @@ Formly is a dynamic (JSON powered) form library for Angular that brings unmatche
65
66
  | Angular >= 4 | `@ngx-formly/core@2.x` |
66
67
  | Angular >= 2 | `ng-formly@1.x` |
67
68
 
69
+ #### Upgrading to v8
70
+
71
+ Formly v8 requires Angular 19 or newer. Upgrade Angular first, then update all
72
+ `@ngx-formly/*` packages used by your application together to v8.
73
+ The Material, PrimeNG, and NG-ZORRO integrations require version 19 or newer of
74
+ their respective UI libraries. Applications staying on Angular 18 should use Formly v7.
75
+
76
+ See the [changelog](CHANGELOG.md) for the full list of changes.
77
+
68
78
  ## Core Team
69
79
 
70
80
  <table>
@@ -272,6 +272,46 @@ class FormlyJsonschema {
272
272
  if (schema.anyOf) {
273
273
  field.fieldGroup.push(this.resolveMultiSchema('anyOf', schema.anyOf, options));
274
274
  }
275
+ // Process if/then/else conditional schemas
276
+ if (options.conditionalSchemas) {
277
+ const conditionalSchemas = options.conditionalSchemas;
278
+ conditionalSchemas.forEach((conditionalSchema) => {
279
+ const condition = conditionalSchema._ifCondition;
280
+ if (condition && conditionalSchema.properties) {
281
+ // Create a field group with the conditional fields
282
+ const conditionalFieldGroup = {
283
+ fieldGroup: [],
284
+ expressions: {
285
+ hide: (f) => {
286
+ if (!f.model) {
287
+ return true;
288
+ }
289
+ const modelValue = f.model[condition.property];
290
+ const matches = modelValue === condition.value;
291
+ // If negate is true (for "else" case), invert the condition
292
+ return condition.negate ? matches : !matches;
293
+ },
294
+ },
295
+ };
296
+ // Add each property from the conditional schema to the field group
297
+ Object.keys(conditionalSchema.properties).forEach((property) => {
298
+ const propSchema = conditionalSchema.properties[property];
299
+ if (!propSchema) {
300
+ return;
301
+ }
302
+ const isRequired = Array.isArray(conditionalSchema.required) && conditionalSchema.required.indexOf(property) !== -1;
303
+ const conditionalField = this._toFieldConfig(propSchema, {
304
+ ...options,
305
+ key: property,
306
+ isOptional: !isRequired,
307
+ resetOnHide: true,
308
+ });
309
+ conditionalFieldGroup.fieldGroup.push(conditionalField);
310
+ });
311
+ field.fieldGroup.push(conditionalFieldGroup);
312
+ }
313
+ });
314
+ }
275
315
  break;
276
316
  }
277
317
  case 'array': {
@@ -378,7 +418,7 @@ class FormlyJsonschema {
378
418
  if (schema.anyOf && !field.type) {
379
419
  delete field.key;
380
420
  field.fieldGroup = [
381
- this.resolveMultiSchema('oneOf', schema.anyOf, { ...options, key, shareFormControl: false }),
421
+ this.resolveMultiSchema('anyOf', schema.anyOf, { ...options, key, shareFormControl: false }),
382
422
  ];
383
423
  }
384
424
  // map in possible formlyConfig options from the widget property
@@ -397,6 +437,14 @@ class FormlyJsonschema {
397
437
  if (schema && schema.allOf) {
398
438
  schema = this.resolveAllOf(schema, options);
399
439
  }
440
+ // Process if/then/else and store conditionals in options for later processing
441
+ if (schema && (schema.if || schema.then || schema.else)) {
442
+ const conditionalSchemas = this.resolveIfThenElse(schema, options);
443
+ if (conditionalSchemas.length > 0) {
444
+ // Store conditional schemas in options for processing in _toFieldConfig
445
+ options.conditionalSchemas = conditionalSchemas;
446
+ }
447
+ }
400
448
  return schema;
401
449
  }
402
450
  resolveAllOf({ allOf, ...baseSchema }, options) {
@@ -427,6 +475,55 @@ class FormlyJsonschema {
427
475
  }, baseSchema);
428
476
  }
429
477
  resolveMultiSchema(mode, schemas, options) {
478
+ const isRecursive = this.isRecursiveMultiSchema(schemas, options);
479
+ const fieldGroup = schemas.map((s, i) => {
480
+ const expressions = {
481
+ hide: (f, forceUpdate) => {
482
+ if (options.skipMultiSchemaValidation) {
483
+ return true;
484
+ }
485
+ const control = f.parent.parent.fieldGroup[0].formControl;
486
+ if (control.value === -1 || forceUpdate) {
487
+ let value = f.parent.fieldGroup
488
+ .map((f, i) => [f, i, this.isFieldValid(f, i, schemas, options, isRecursive)])
489
+ .sort(([f1, i1, f1Valid], [f2, i2, f2Valid]) => {
490
+ if (f1Valid !== f2Valid) {
491
+ return f2Valid ? 1 : -1;
492
+ }
493
+ const schemaField1 = isRecursive ? this.getSchemaField(schemas[i1]) || f1 : f1;
494
+ const schemaField2 = isRecursive ? this.getSchemaField(schemas[i2]) || f2 : f2;
495
+ const matchedFields1 = totalMatchedFields(schemaField1);
496
+ const matchedFields2 = totalMatchedFields(schemaField2);
497
+ if (matchedFields1 === matchedFields2) {
498
+ if (schemaField1.props?.disabled === schemaField2.props?.disabled) {
499
+ return 0;
500
+ }
501
+ return matchedFields2 > matchedFields1 ? 1 : -1;
502
+ }
503
+ return matchedFields2 > matchedFields1 ? 1 : -1;
504
+ })
505
+ .map(([, i]) => i);
506
+ if (mode === 'anyOf') {
507
+ const definedValue = value.filter((i) => totalMatchedFields(f.parent.fieldGroup[i]));
508
+ value = definedValue.length > 0 ? definedValue : [value[0] || 0];
509
+ }
510
+ value = value.length > 0 ? value : [0];
511
+ control.setValue(mode === 'anyOf' ? value : value[0]);
512
+ }
513
+ const hide = Array.isArray(control.value) ? control.value.indexOf(i) === -1 : control.value !== i;
514
+ if (isRecursive && !hide) {
515
+ this.ensureMultiSchemaField(f, s, options);
516
+ }
517
+ return hide;
518
+ },
519
+ };
520
+ return isRecursive
521
+ ? this.createMultiSchemaField(s, options, expressions)
522
+ : {
523
+ ...this._toFieldConfig(s, { ...options, resetOnHide: true }),
524
+ expressions,
525
+ };
526
+ });
430
527
  return {
431
528
  type: 'multischema',
432
529
  fieldGroup: [
@@ -442,44 +539,114 @@ class FormlyJsonschema {
442
539
  },
443
540
  },
444
541
  {
445
- fieldGroup: schemas.map((s, i) => ({
446
- ...this._toFieldConfig(s, { ...options, resetOnHide: true }),
447
- expressions: {
448
- hide: (f, forceUpdate) => {
449
- const control = f.parent.parent.fieldGroup[0].formControl;
450
- if (control.value === -1 || forceUpdate) {
451
- let value = f.parent.fieldGroup
452
- .map((f, i) => [f, i, this.isFieldValid(f, i, schemas, options)])
453
- .sort(([f1, , f1Valid], [f2, , f2Valid]) => {
454
- if (f1Valid !== f2Valid) {
455
- return f2Valid ? 1 : -1;
456
- }
457
- const matchedFields1 = totalMatchedFields(f1);
458
- const matchedFields2 = totalMatchedFields(f2);
459
- if (matchedFields1 === matchedFields2) {
460
- if (f1.props.disabled === f2.props.disabled) {
461
- return 0;
462
- }
463
- return matchedFields2 > matchedFields1 ? 1 : -1;
464
- }
465
- return matchedFields2 > matchedFields1 ? 1 : -1;
466
- })
467
- .map(([, i]) => i);
468
- if (mode === 'anyOf') {
469
- const definedValue = value.filter((i) => totalMatchedFields(f.parent.fieldGroup[i]));
470
- value = definedValue.length > 0 ? definedValue : [value[0] || 0];
471
- }
472
- value = value.length > 0 ? value : [0];
473
- control.setValue(mode === 'anyOf' ? value : value[0]);
474
- }
475
- return Array.isArray(control.value) ? control.value.indexOf(i) === -1 : control.value !== i;
476
- },
477
- },
478
- })),
542
+ fieldGroup,
479
543
  },
480
544
  ],
481
545
  };
482
546
  }
547
+ createMultiSchemaField(schema, options, expressions) {
548
+ return {
549
+ expressions,
550
+ hooks: {
551
+ onInit: (f) => {
552
+ if (f.hide === false) {
553
+ this.ensureMultiSchemaField(f, schema, options);
554
+ }
555
+ },
556
+ },
557
+ };
558
+ }
559
+ ensureMultiSchemaField(field, schema, options) {
560
+ if (field._jsonSchemaFormlyField) {
561
+ return;
562
+ }
563
+ const expressions = field.expressions;
564
+ const hooks = field.hooks;
565
+ const mutableField = field;
566
+ delete mutableField['formControl'];
567
+ delete mutableField['_keyPath'];
568
+ Object.keys(field).forEach((prop) => {
569
+ delete mutableField[prop];
570
+ });
571
+ Object.assign(field, this._toFieldConfig(schema, { ...options, resetOnHide: true }));
572
+ field.expressions = expressions;
573
+ field.hooks = hooks;
574
+ Object.defineProperty(field, '_jsonSchemaFormlyField', {
575
+ value: true,
576
+ configurable: true,
577
+ });
578
+ field.options?.build(field);
579
+ }
580
+ getSchemaField(schema) {
581
+ return schema._field?.fieldGroup?.[0];
582
+ }
583
+ isRecursiveMultiSchema(schemas, options) {
584
+ const visited = new WeakSet();
585
+ const visit = (schema) => {
586
+ if (!schema || typeof schema !== 'object') {
587
+ return false;
588
+ }
589
+ if (visited.has(schema)) {
590
+ return false;
591
+ }
592
+ visited.add(schema);
593
+ if (schema.$ref) {
594
+ const definition = this.getDefinition(schema.$ref, options);
595
+ return !!definition && (definition.oneOf === schemas || definition.anyOf === schemas || visit(definition));
596
+ }
597
+ if (schema.oneOf === schemas || schema.anyOf === schemas) {
598
+ return true;
599
+ }
600
+ const children = [];
601
+ if (schema.properties) {
602
+ children.push(...Object.values(schema.properties));
603
+ }
604
+ if (schema.items) {
605
+ children.push(...(Array.isArray(schema.items) ? schema.items : [schema.items]));
606
+ }
607
+ if (schema.additionalItems && typeof schema.additionalItems === 'object') {
608
+ children.push(schema.additionalItems);
609
+ }
610
+ if (schema.allOf) {
611
+ children.push(...schema.allOf);
612
+ }
613
+ if (schema.oneOf && schema.oneOf !== schemas) {
614
+ children.push(...schema.oneOf);
615
+ }
616
+ if (schema.anyOf && schema.anyOf !== schemas) {
617
+ children.push(...schema.anyOf);
618
+ }
619
+ if (schema.dependencies) {
620
+ Object.values(schema.dependencies).forEach((dependency) => {
621
+ if (dependency && !Array.isArray(dependency)) {
622
+ children.push(dependency);
623
+ }
624
+ });
625
+ }
626
+ ['not', 'if', 'then', 'else'].forEach((key) => {
627
+ const child = schema[key];
628
+ if (child && typeof child === 'object') {
629
+ children.push(child);
630
+ }
631
+ });
632
+ return children.some(visit);
633
+ };
634
+ return schemas.some(visit);
635
+ }
636
+ getDefinition(ref, options) {
637
+ const [uri, pointer] = ref.split('#/');
638
+ if (uri || !pointer) {
639
+ return null;
640
+ }
641
+ let definition = options.schema;
642
+ for (const path of pointer.split('/')) {
643
+ if (!definition || typeof definition !== 'object' || !definition.hasOwnProperty(path)) {
644
+ return null;
645
+ }
646
+ definition = definition[path];
647
+ }
648
+ return typeof definition === 'object' ? definition : null;
649
+ }
483
650
  resolveDefinition(schema, options) {
484
651
  const [uri, pointer] = schema.$ref.split('#/');
485
652
  if (uri) {
@@ -529,6 +696,58 @@ class FormlyJsonschema {
529
696
  });
530
697
  return { propDeps, schemaDeps };
531
698
  }
699
+ extractIfCondition(ifSchema) {
700
+ // Extract the property and const value from the "if" schema
701
+ // Supports: { "properties": { "propName": { "const": value } } }
702
+ if (ifSchema.properties) {
703
+ const propName = Object.keys(ifSchema.properties)[0];
704
+ if (propName) {
705
+ const propSchema = ifSchema.properties[propName];
706
+ if (propSchema && propSchema.hasOwnProperty('const')) {
707
+ return { property: propName, value: propSchema.const };
708
+ }
709
+ }
710
+ }
711
+ return null;
712
+ }
713
+ resolveIfThenElse(schema, options) {
714
+ // Process if/then/else and return array of conditional schemas to add to fieldGroup
715
+ const conditionalSchemas = [];
716
+ if (schema.if && typeof schema.if === 'object') {
717
+ const condition = this.extractIfCondition(schema.if);
718
+ if (condition) {
719
+ // Process "then" branch
720
+ if (schema.then && typeof schema.then === 'object') {
721
+ const resolvedSchema = this.resolveConditionalSchema(schema.then, options);
722
+ conditionalSchemas.push({
723
+ ...resolvedSchema,
724
+ _ifCondition: condition,
725
+ });
726
+ }
727
+ // Process "else" branch
728
+ if (schema.else && typeof schema.else === 'object') {
729
+ const resolvedSchema = this.resolveConditionalSchema(schema.else, options);
730
+ conditionalSchemas.push({
731
+ ...resolvedSchema,
732
+ _ifCondition: { property: condition.property, value: condition.value, negate: true },
733
+ });
734
+ }
735
+ }
736
+ }
737
+ return conditionalSchemas;
738
+ }
739
+ resolveConditionalSchema(conditionalSchema, options) {
740
+ // Resolve $ref and allOf if present in conditional schema
741
+ // Note: We don't use resolveSchema here to avoid recursive processing of nested if/then/else
742
+ let resolved = conditionalSchema;
743
+ if (conditionalSchema.$ref) {
744
+ resolved = this.resolveDefinition(conditionalSchema, options);
745
+ }
746
+ if (resolved.allOf) {
747
+ resolved = this.resolveAllOf(resolved, options);
748
+ }
749
+ return resolved;
750
+ }
532
751
  guessSchemaType(schema) {
533
752
  const type = schema?.type;
534
753
  if (!type && schema?.properties) {
@@ -575,7 +794,7 @@ class FormlyJsonschema {
575
794
  }
576
795
  return this.toEnumOptions(schema.items);
577
796
  }
578
- isFieldValid(root, i, schemas, options) {
797
+ isFieldValid(root, i, schemas, options, skipMultiSchemaValidation = false) {
579
798
  const schema = schemas[i];
580
799
  if (!schema._field) {
581
800
  Object.defineProperty(schema, '_field', { enumerable: false, writable: true, configurable: true });
@@ -594,6 +813,7 @@ class FormlyJsonschema {
594
813
  ...options,
595
814
  resetOnHide: true,
596
815
  ignoreDefault: true,
816
+ skipMultiSchemaValidation,
597
817
  map: null,
598
818
  }),
599
819
  ],
@@ -619,10 +839,10 @@ class FormlyJsonschema {
619
839
  }
620
840
  return f1;
621
841
  }
622
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FormlyJsonschema, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
623
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FormlyJsonschema, providedIn: 'root' }); }
842
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormlyJsonschema, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
843
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormlyJsonschema, providedIn: 'root' }); }
624
844
  }
625
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FormlyJsonschema, decorators: [{
845
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: FormlyJsonschema, decorators: [{
626
846
  type: Injectable,
627
847
  args: [{ providedIn: 'root' }]
628
848
  }] });