Skip to content

Commit

Permalink
fix(json-schema): improve oneOf selection of mixed type (#3248)
Browse files Browse the repository at this point in the history
fix #3245
  • Loading branch information
aitboudad committed Mar 30, 2022
1 parent 0ce8013 commit 39d41a3
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 12 deletions.
18 changes: 18 additions & 0 deletions src/core/json-schema/src/formly-json-schema.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1099,6 +1099,24 @@ describe('Service: FormlyJsonschema', () => {
expect(foo2Field.hide).toBeFalsy();
});

it('should support oneOf using mixed type', () => {
const { field } = renderComponent({
model: { foo: [] },
schema: {
type: 'object',
oneOf: [
{ properties: { foo: { type: 'object' } } },
{ properties: { foo: { type: 'array' } } },
],
},
});

const [, { fieldGroup: [foo1Field, foo2Field] }] = field.fieldGroup[0].fieldGroup;

expect(foo1Field.hide).toBeTruthy();
expect(foo2Field.hide).toBeFalsy();
});

it('should support nested oneOf', () => {
const { field, model } = renderComponent({
model: { foo: 2 },
Expand Down
40 changes: 28 additions & 12 deletions src/core/json-schema/src/formly-json-schema.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { FormlyFieldConfig } from '@ngx-formly/core';
import { JSONSchema7, JSONSchema7TypeName } from 'json-schema';
import { AbstractControl, FormGroup } from '@angular/forms';
import { AbstractControl, FormArray, FormGroup } from '@angular/forms';
import {
ɵreverseDeepMerge as reverseDeepMerge,
ɵgetFieldValue as getFieldValue,
Expand Down Expand Up @@ -48,9 +48,23 @@ function totalMatchedFields(field: FormlyFieldConfig): number {
}

const total = field.fieldGroup.reduce((s, f) => totalMatchedFields(f) + s, 0);
return total === 0
? field.key && getFieldValue(field) !== undefined ? 1 : 0
: total;
if (total === 0 && field.key) {
const value = getFieldValue(field);
if (
value === null
|| (
(value !== undefined)
&& (
(field.fieldArray && Array.isArray(value))
|| (!field.fieldArray && isObject(value))
)
)
) {
return 1;
}
}

return total;
}

interface IOptions extends FormlyJsonschemaOptions {
Expand Down Expand Up @@ -393,11 +407,12 @@ export class FormlyJsonschema {
const control = f.parent.parent.fieldGroup[0].formControl;
if ((control.value === -1) || forceUpdate) {
let value = f.parent.fieldGroup
.map((f, i) => [f, i] as [FormlyFieldConfig, number])
.filter(([f, i]) => {
return this.isFieldValid(f, schemas[i], options);
})
.sort(([f1], [f2]) => {
.map((f, i) => [f, i, this.isFieldValid(f, schemas[i], options)] as [FormlyFieldConfig, number, boolean])
.sort(([f1, i1, f1Valid], [f2, i2, f2Valid]) => {
if (f1Valid !== f2Valid) {
return f2Valid ? 1 : -1;
}

const matchedFields1 = totalMatchedFields(f1);
const matchedFields2 = totalMatchedFields(f2);
if (matchedFields1 === matchedFields2) {
Expand Down Expand Up @@ -506,7 +521,7 @@ export class FormlyJsonschema {
return type;
}

private addValidator(field: FormlyFieldConfig, name: string, validator: (control: AbstractControl) => boolean) {
private addValidator(field: FormlyFieldConfig, name: string, validator: (control: AbstractControl, field: FormlyFieldConfig) => boolean) {
field.validators = field.validators || {};
field.validators[name] = validator;
}
Expand Down Expand Up @@ -545,10 +560,11 @@ export class FormlyJsonschema {
}

private isFieldValid(field: FormlyFieldConfig, schema: JSONSchema7, options: IOptions): boolean {
const model = field.model ? clone(field.model) : (field.fieldArray ? [] : {});
const { form } = (field.options as any)._buildField({
form: new FormGroup({}),
form: Array.isArray(model) ? new FormArray([]) : new FormGroup({}),
fieldGroup: [this._toFieldConfig(schema, { ...options, resetOnHide: true, ignoreDefault: true, map: null })],
model: field.model ? clone(field.model) : (field.fieldArray ? [] : {}),
model,
});

return form.valid;
Expand Down

0 comments on commit 39d41a3

Please sign in to comment.