I’ve added an object-level custom keyword to ajv that checks whether a required string property of an object not only defined + a string but also is not just an empty string. Example:
That produces an error when the trimmed value for someString is an empty string in the same format as the required ones, where the Error’s instancePath is the object path and the property name is shared via a param:
The validation all works well but is not being mapped to the input in the form. I can pinpoint the source of the breakdown to some special handling for required keyword messages:
But that’s deeply nested within a large number of functions, path it:
withJsonFormsControlProps > withContextToControlProps > ctxToControlProps > mapStateToControlProps > [various small wrapper layers omitted for brevity] > getControlPath > getInvalidProperty
We already have a number of custom wrappers around several withJsonForms...
Seeing as the customization seems to be deeply nested in a very low-level function is there a straightforward way to add support for this? I know that an alternative would be to make this a property-level validation which would fix this error path issue but it would remove the ability to reference the specific property in the error .message.
To customize the error message you can use JSON Forms’ i18n support. It allows to adapt the message for each separate validation error and/or combine all validation errors for a single property into a single message. See here for the documentation.
If you want to go forward with the custom validation on object level approach, then there is currently no way around either using custom renderers or at minimum custom bindings.
Within the renderers (or your custom binding) you can use useJsonForms() to get full access to the JSON Forms state including all errors. Therefore you can also find the custom error of yours to additionally hand it over to your renderers.
We already have a bunch of custom renderers and bindings so that could work but it seems like way a lot effort, especially since I can get most of the way there just making the custom keyword apply to type: 'string' schema instead of the parent type: 'object' schema.
We wanted to avoid using minLength: 1 since that still considers a single whitespace character as valid. I guess we could trim the user input passed to handleChange, tho.
Is it that much effort? This only needs to be done for the string renderer, right?
Generally speaking, without knowing your requirements, it feels more natural to apply the keyword on the actual properties, so I think that’s a good solution.
it feels more natural to apply the keyword on the actual properties
Agreed! I started there and then wanted to match the way that the OOTB ajv required error messages work. Our implementation is a bit complex, high-level:
Dealing with arbitrarily deeply nested object and array schemas so we render pieces of the schema, navigable via a tree view.
We’re also exposing an banner at the top of the page that displays all the errors with links to the the related tree node
We’ve built a bunch of custom annotations to handle UI enhancements and customizations beyond what we felt like we were able to easily do with the included uischema functionality
We’re already wrapping our custom renderers in functionality to merge the control props with our custom annotation props and adding complexity here feels like something to carefully weigh (all the primitive types also share a wrapper so needing to add a custom text one is preferably avoided)
Anyway, the difference vs the required vs custom (or minLength) keyword on the actual property ends up being:
“Metadata must have required property ‘Internal Name’”
vs
“Internal Name is a required property”
Which, despite some incongruity and a small degradation in ux, is an acceptable tradeoff in the short term (since the messages link to the property anyway).