Control validation behaviour and include server-side errors; recommended approach (Vue 3)

First thanks for this great library. We are building a enterprise app with lots of forms and we are using this library to generate complex form/UI. The app is build using Vue 3 and we are developing a custom renderer set.

One thing we are running into is how to control validation. Ideally we would like to support the following:

  • Initially no validation is shown. After a form field is ‘left’ (blur event), the errors are shown for the field if any.
  • When a form is “submitted” (handled outside of JsonForms library), all errors need to be shown. For example if a field is never touched but is required; upon submit the “this field is required” message needs to be shown.
  • As a response to the form submit, server side errors may be returned. These need to be shown in the form so ideally these need to be ‘merged’ with the JsonForms error state.

Some of the above is addressed to some extend in other topics. However I am wondering what is currently the recommended approach to solve it, maybe there are some new features on the roadmap that can be used in the near future?

I implemented an “isTouched” state using custom renderers. This works fine however the requirement of showing all errors (also untouched fields) upon submit still needs a solution. Same is for handling server side errors.

I am looking into Vue’s provide/inject to make “form state” data available to all renderers, this ‘form state’ data may include isSubmitted: true/false and serverErrors.

Do you have any suggestions and are there any upcoming features in JsonForm that may simplify a solution? Thanks in advance!

Hi @jdwit, Thanks for your compliments.

At the moment we have three built-in form wide validation modes, you can find the documentation here. Sadly they are not a perfect fit for your case so I would stick with the default mode in your case.

Our current off-the-shelf renderer sets don’t support the “touch” behavior. However as you’re implementing your own renderer set anyway, you can easily add this.

I implemented an “isTouched” state using custom renderers. This works fine however the requirement of showing all errors (also untouched fields) upon submit still needs a solution. Same is for handling server side errors.

I am looking into Vue’s provide/inject to make “form state” data available to all renderers, this ‘form state’ data may include isSubmitted: true/false and serverErrors.

That’s exactly my suggestion:

  • Manage a isTouched state in each renderer and simply ignore the errors prop until touched is true.
  • Manage a forceShow setting (for example via provide/inject) and if true, ignore the touched state and show the errors anyway. This can be provided outside of JSON Forms.
  • Depending on your requirements forceShow might also then set all touched to true etc. Whatever fits here for you.

We could think about adding a new validation mode to JSON Forms, like validateAndForceShow, or maybe validateAndShowOnTouch. Then you would not need to handle this yourself. However this is not there yet, so for now a simple forceShow injection is enough.

What’s more difficult is the handling of external errors. There is currently no great built-in solution for that as JSON Forms currently assumes full control of the errors and does not offer an option to customize it. We are looking into this ourselves, a simple solution could be to mixin AJV-style errors via a prop to JSON Forms. However I can’t give any ETA for this.

However for you a solution is relatively easy: You can simply add this to your renderer set. As you implement and bind all of your renderers anyway you can add a custom binding to each renderer which consumes your custom errors (again via provide/inject) and just concatenates them to the errors determined by JSON Forms. You should be able to implement this generically, so depending on your custom error format this might be very simple.

Your custom binding can wrap the JSON Forms binding, use all of the determined props and then add the custom error and touched handling on top. In the end I would expect something in the order of 50 lines of binding code (for both features), reused in all renderers.

Hi stefan, thanks for the reply. I will take exactly the approach you’re suggesting: manage a isTouched state per control, manage a forceShowErrors setting (injected) and manage a serverErrors state. These are all done in the custom binding.

Another thing not related to this: the i18n feature recently added to JSONForms is really useful, this make it a ton more useful for me. The thing I am wondering here: it would be really useful if the translate function besides key and defaultMessage will also additional arguments; for example when the validation requirement is format: "email" I can somehow resolve this to my correct translation key, or when the JSON schema contains a minLength, the value of minLength can be provided to my translation implementation to generate a message such as "This field must have at least {minLength} characters.

I was looking into the source regarding i18n but I didn’t find an obvious solution yet. Again the i18n is a great feature!

Hey @jdwit!

We are also using the i18n feature, and have similar requirements to you.

I’m guessing you are already integrating your own translator (something like formatjs)? What I think would be really useful would be, like you say, to be able to pass in other data to the translator function.

At the moment, the translator only supports simple ‘string to string’ translation, and has no awareness of the need to pass in anything other than the key and the defaultMessage.

What will be interesting is - working out what other arguments could be passed in and how. I know that the renderers get passed the schema and uiSchema ‘section’ that applies to them, so that might be a way in.

Also worth considering is how this would interact with the error translator - that already does some falling-back to the ajv-errors (and they get their dynamic error values from somewhere, presumably as a result of parsing the whole schema to validate it).

You might find our i18n implementation useful for reference - just as an example of another Vue app (we are Vue2 and using the Vuetify Renderer set with the Vuetify UI library).

I’d also recommend starting an issue over at the github repo for this, as it would be a great additional functionality.

1 Like