Displaying Nested Errors

The nested object error is not displaying
Schema: homePhone: { type: "object", required: ["phone_number", "country_code", "country"], properties: { phone_number: { type: "string", minLength: 1 }, country_code: { type: "string" }, country: { type: "string" }, }, },
UISchema:

    {
              type: "Control",
              scope: "#/properties/homePhone",
              renderer: "PhoneInputControl",
            },

The error gets generated, but it is not available within the props of the component:
This is the error that was logged:

{
    "instancePath": "/homePhone/phone_number",
    "schemaPath": "#/properties/homePhone/properties/phone_number/minLength",
    "keyword": "minLength",
    "params": {
        "limit": 1
    },
    "message": "must NOT have fewer than 1 characters",
    "schema": 1,
    "parentSchema": {
        "type": "string",
        "minLength": 1
    },
    "data": ""
}

These are the ControlProps that were logged out:

{
    "uischema": {
        "type": "Control",
        "scope": "#/properties/homePhone",
        "renderer": "PhoneInputControl"
    },
    "schema": {
        "type": "object",
        "required": [
            "phone_number",
            "country_code",
            "country"
        ],
        "properties": {
            "country": {
                "type": "string"
            },
            "country_code": {
                "type": "string"
            },
            "phone_number": {
                "type": "string",
                "minLength": 1
            }
        }
    },
    "path": "homePhone",
    "enabled": true,
    "renderers": [
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {}
    ],
    "cells": [
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {},
        {}
    ],
    "id": "#/properties/homePhone",
    "data": {
        "phone_number": "",
        "country_code": "1",
        "country": "us"
    },
    "errors": "",
    "label": "Home Phone",
    "visible": true,
    "required": true,
    "config": {
        "restrict": false,
        "trim": false,
        "showUnfocusedDescription": false,
        "hideRequiredAsterisk": false
    },
    "rootSchema": {
        "type": "object",
        "required": [
            "lastName",
            "firstName",
            "email",
            "homePhone",
            "mobilePhone",
            "termsOfService"
        ],
        "properties": {
            "zip": {
                "type": "string"
            },
            "city": {
                "type": "string"
            },
            "email": {
                "type": "string"
            },
            "state": {
                "type": "string"
            },
            "title": {
                "enum": [
                    "Mr",
                    "Mrs",
                    "Ms",
                    "Rabbi",
                    "Reb",
                    "Rebbetzin",
                    "Dr"
                ],
                "type": [
                    "string",
                    "null"
                ]
            },
            "gender": {
                "enum": [
                    "Male",
                    "Female"
                ],
                "type": [
                    "string",
                    "null"
                ]
            },
            "address": {
                "type": "string"
            },
            "company": {
                "type": "string"
            },
            "lastName": {
                "type": "string"
            },
            "optToSms": {
                "type": "boolean"
            },
            "firstName": {
                "type": "string"
            },
            "homePhone": {
                "type": "object",
                "required": [
                    "phone_number",
                    "country_code",
                    "country"
                ],
                "properties": {
                    "country": {
                        "type": "string"
                    },
                    "country_code": {
                        "type": "string"
                    },
                    "phone_number": {
                        "type": "string",
                        "minLength": 1
                    }
                }
            },
            "mobilePhone": {
                "type": "object",
                "required": [
                    "country_code",
                    "phone_number",
                    "country"
                ],
                "properties": {
                    "country": {
                        "type": "string"
                    },
                    "country_code": {
                        "type": "string"
                    },
                    "phone_number": {
                        "type": "string",
                        "minLength": 1
                    }
                }
            },
            "optToEmails": {
                "type": "boolean"
            },
            "termsOfService": {
                "type": "boolean"
            },
            "aliyahInformation": {
                "type": "object",
                "properties": {
                    "kohenLeviYisroel": {
                        "enum": [
                            "Kohen",
                            "Levi",
                            "Yisroel"
                        ],
                        "type": "string"
                    },
                    "last_name_hebrew": {
                        "type": "string",
                        "title": "שם משפחה"
                    },
                    "first_name_hebrew": {
                        "type": "string",
                        "title": "שם"
                    },
                    "father_name_hebrew": {
                        "type": "string",
                        "title": "שם האב"
                    },
                    "mother_name_hebrew": {
                        "type": "string",
                        "title": "שם האם"
                    }
                }
            }
        }
    }
}

Hi!

The error is correctly shown for me

Which JSON Forms version, binding and renderers are you using?

@jsonforms/core”: “^3.0.0”,
@jsonforms/material-renderers”: “^3.0.0”,
@jsonforms/react”: “^3.0.0”,

I am using a control input control - source code below:
I tried logging out the errors, but it returns an empty array.
However, I do know that the error is getting generated as the submit button is greyed out if the input contains less than the required amount of digits

import { withJsonFormsControlProps } from "@jsonforms/react";
import { ControlProps } from "@jsonforms/core";
import { PhoneInput } from "../../../../components/inputs/PhoneInput";

const PhoneInputControl = (props: ControlProps) => {
  const { data, handleChange, path, label, errors } = props;
  console.log("errors", errors);

  return (
    <PhoneInput
      value={data?.phone_number}
      onChange={(phone, country_code, country) => {
        if (phone.replace(`+${country_code}`, "") || data?.phone_number) {
          handleChange(path, {
            phone_number: phone.replace(`+${country_code}`, ""),
            country_code,
            country,
          });
        }
      }}
      countryId={""}
      variant="standard"
      label={label}
      fullWidth
      error={errors ? true : false}
    />
  );
};

export default withJsonFormsControlProps(PhoneInputControl);

Hi @MRides,

It seems that your custom controls manages the whole object and not only the phone number. At the moment we do not return all sub errors of the object via the bindings, just errors related to the object itself.

You should be able to determine the child errors like this:

const jsonforms = useJsonForms();
const childErrors = getSubErrorsAt(path, props.schema)(jsonforms);

Thanks for your help. Here is my updated component

import { withJsonFormsControlProps, useJsonForms } from "@jsonforms/react";
import { ControlProps, getSubErrorsAt } from "@jsonforms/core";
import { PhoneInput } from "../../../../components/inputs/PhoneInput";

const PhoneInputControl = (props: ControlProps) => {
  const { data, handleChange, path, label, errors } = props;
  const jsonforms: any = useJsonForms();
  const childErrors = getSubErrorsAt(path, props.schema)(jsonforms);
  console.log("childErrors", childErrors);
  console.log("phonedata", data);
  console.log("props", { ...props });
  return (
    <PhoneInput
      value={data?.phone_number}
      onChange={(phone, country_code, country) => {
        if (phone.replace(`+${country_code}`, "") || data?.phone_number) {
          handleChange(path, {
            phone_number: phone.replace(`+${country_code}`, ""),
            country_code,
            country,
          });
        }
      }}
      countryId={""}
      variant="standard"
      label={label}
      fullWidth
      error={errors ? true : false}
    />
  );
};

export default withJsonFormsControlProps(PhoneInputControl);

This is causing the following error:
Uncaught TypeError: Cannot read properties of undefined (reading ‘core’)

(Note that I had to set the type of jsonforms to any, otherwise I got the following ts error:
Argument of type ‘JsonFormsStateContext’ is not assignable to parameter of type ‘JsonFormsState’.
Property ‘jsonforms’ is missing in type ‘JsonFormsStateContext’ but required in type ‘JsonFormsState’
)

My code snippet was not accurate, this is how you need to invoke getSubErrorsAt:

const jsonforms = useJsonForms();
const childErrors = getSubErrorsAt(path, props.schema)({ jsonforms });

Hello,
That works if the hierarchy structure in the forms follow the one from the JSON Schema.
But if there is Category that contains Control from different scope (path) anywhere in the JSONSchema, how to get from the Category Renderer all errors from the sub controls.
The idea is to display on a tab, the number of errors inside a tab.
I have a prototype working just by doing a basic query selector on the Dom to get the sub Elements error but I would like to see if it is achievement by using JsonForms Code.
Thanks.

Hi @srichez888,

Sadly we don’t offer a built-in mechanism for this use case. You will need to iterate through the UI Schema and collect all errors of all nested controls manually then.

Hello,

I have been able to have something working by doing the following in the category renderer to achieve such UI
Capture

const hasNestedError = (category):boolean =>  {
      const jsonforms = useJsonForms();
      const errorSchemaPaths  = jsonforms.core.errors.map(x=>x.schemaPath);
      const nestedUISchemaScopes = getNestedUISchemaScopes(category.value.uischema);
      return errorSchemaPaths.some( errorSchemaPath => 
         nestedUISchemaScopes.some(nestedUISchemaScope => 
            errorSchemaPath.startsWith(nestedUISchemaScope)
      ));
    }
export const getNestedUISchemaScopes = (uischema:Layout):string[] => {
  const result:string[] = [];
  const scan = (uischema:Layout) => {
    for(const subUISchema of uischema.elements) {
      if (isLayout(subUISchema)) 
         scan(subUISchema);
      else {
        if (isControl(subUISchema)) result.push(subUISchema.scope);
      }
    }  
  }
  scan(uischema);
  return result;
}

The code is based on all errors but I should only care about error under the current instancePath but I didn’t know how.
Thanks for your help.

Regards,
Sebastien