Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow the insertion of errors from outside JSONSchema validation #155

Closed
nuclearspike opened this issue Apr 21, 2016 · 30 comments
Closed

Allow the insertion of errors from outside JSONSchema validation #155

nuclearspike opened this issue Apr 21, 2016 · 30 comments

Comments

@nuclearspike
Copy link

It's great to be able to define a regex to validate an input, however, that regex should really not be shown to the user. I'd like to be able to define an error string "Your username must only contain letters, numbers and underscores" rather than having it display the regex it isn't matching... no average user can understand what it's looking for from displaying a regex.

@nuclearspike
Copy link
Author

Oops. errorSchema appears to cover this.

@nuclearspike
Copy link
Author

Nope, I don't think it does. I thought that was a way to provide error messages.

@nuclearspike nuclearspike reopened this Apr 24, 2016
@n1k0
Copy link
Collaborator

n1k0 commented Apr 24, 2016

You're right, we must find a way to override default error messages. Unfortunately, there doesn't seem to be any documented way to achieve just that in jsonschema, which is what we use for JSONSchema validation :/

@nuclearspike
Copy link
Author

The same way this component has the uiSchema, it'd be great to provide a way to define error messages where it's something outside of the scope of jsonschema. I've had to just set the .panel.errors to display:none because of all of the "instance" stuff that is not end-user-ready and that it is not using the "title" for a field. Something like "instance.first_name" is not ok to display to a user. So my solution is I just let it put the errors under the fields and hide the error list and avoid regex validation.

I should also be able to include my own error messages. For example, I created component that wraps this one that is designed to work with standard REST endpoints and Rails error messages. I'd love to be able to tell the component that "email" is already in use and have that error message appear under the email field just as it would for jsonschema validation errors. The error back from Rails has the field name that (in this common situation) matches the schema field name.

I've seen other comments about wanting to be able to perform validations yourself, that are outside of what jsonschema validations supports (passwords must match, etc). I'd like to see an a prop that allows you to provide an externally derived errorSchema that gets blended with what the component came up with on it's own. Devs could either have it touch the server and generate an errorSchema to display or just have additional client-side validations that get mixed in... this is probably a separate issue. But this would let me work around the issues I am having.

@n1k0
Copy link
Collaborator

n1k0 commented Apr 24, 2016

We definitely want to add support for custom validation with associated relevant custom error messages. "Blending" these things with what our jsonschema validation lib exposes as data and API can get tricky though.

Todo:

  • Accept an errorSchema prop to merge with the form internal one so you can inform it some fields are in an error state according to some external validation rule; the issue is how could we possibly know when such fields are not invalid anymore?
  • Accept some validationSchema object to define external validation rules for fields (as suggested in Custom field validation #145);

I have a feeling the right way of solving all this would be to extract the whole validation system from the component internals, so users would be able to augment it with their own rules, messages and so on.

Something like this (dumb code):

import Form, { validateJsonSchema, mergeErrorSchema } from "react-jsonschema-form";

const schema = {
  type: "object",
  properties: {
    pass1: {type: "string"},
    pass2: {type: "string"}
  }
};

const validate = ({formData}) => {
  const errorSchema = validateJsonSchema(schema, formData);
  const {pass1, pass2} = formData;
  if (pass1 !== pass2) {
    return mergeErrorSchema(errorSchema, {
      pass2: {
        errors: ["Passwords don't match"]
      }
    });
  }
  return errorSchema;
};

<Form schema={schema} validate={validate} />

Also if we go that route we probably need to allow performing async validation, so we need to support returning a Promise.

Edit: I realize this doesn't even address how we override the jsonschema default error messages. Duh.

@n1k0
Copy link
Collaborator

n1k0 commented Apr 24, 2016

Edit: I realize this doesn't even address how we override the jsonschema default error messages. Duh.

It seems we actually have access to all the arguments we need to expose an api to define custom error messages from the error objects jsonschema returns: tdegrunt/jsonschema#147 (comment)

Now back to square one for this very issue: what kind of API should we expose to leverage custom jsonschema error messages?

@nuclearspike
Copy link
Author

nuclearspike commented Apr 25, 2016

So, the types of validations I would need (and think this covers all):

  1. jsonschema validations. (ex: MinLength, required fields, etc.) Prevents submission.
  2. Client-side synchronous validations. (ex: Passwords don't match, etc.) Prevents submission.
  3. Server-side/client-side async validations. (ex: Username is already taken, etc.) Pre-submission, prevents submission.
  4. Server-side rejections. (ex: Rails 422 unprocessable entity error... any types of errors that prevented saving the record.) Happens after data submission, no changes made to DB, errors need to display inline and identically to jsonschema errors.
    ~5) Overriding the default messages. "instance.password_confirmation" is not something that is presentable to a user. The regex are much worse.

Currently this component only supports type 1.

Granted, this component's scope doesn't cover posting to the server, so it just needs to have the right events and props to allow a user to do that part on their own.

So, one option would be to have a canSubmit/isValid prop event, when if present is evaluated either as a boolean or a Promise for a boolean. Then have a prop for external errors that can be set by any part of that process (or a different process entirely).

I don't like the proposal that the API having an onValidate event that expects a return of the error schema because it limits when I can set it. Just let me generate my list however and whenever I want. Many times this will be when the event fires to validate, but possibly also after I submit to the server and get error messages back. Using the pattern I described above (with pseudocode below) would cover the types 1-4 listed above, and provide me a workaround for #5 (by just keeping some validations out of jsonschema and do them client side with my own error messages).

const schema = {...}
...
  validateEvent({formData}) {
    return fetch(...call to server to check username availability)
       .then(...set the state to update external errors, returns FALSE if there are errors. but throwing an error may be much better than just returning false, as it's much more deliberate)
  }
  onSubmit(...) {
     fetch(...do post to endpoint to save)
       .catch(...set the state to show server errors; this.setState({externalErrors}))
       .then(...move to next page, give message that data posted, etc)
  }
  render() {
   const {externalErrors} = this.state
   return  <Form schema={schema}  ....
       onValidate={this.validateEvent.bind(this)} 
       onSubmit={this.onSubmit.bind(this)}
       externalErrors={externalErrors}  />
  }

I'm thinking that part of the Error Schema should be whether the error should prevent submission. Errors that don't have any client-side catches or async server checks would need to allow the form to post since it will be during the post that it gets rejected and the errors returned. Passwords not matching would be something that prevents form submission, however, but a spam filter that only evaluates during form submission would not prevent submission since it is only evaluated during the submit process. The above pattern would support all of the types mentioned.

However, this does raise problems for when the pre-submit validations should occur. Checking username availability you'd want to have some delay and not check for every single keystroke... so things can get more complex given certain situations.

@Chachaproper
Copy link
Contributor

Is it possible to disable validation now?

@n1k0
Copy link
Collaborator

n1k0 commented Jun 20, 2016

It's possible to disable live validation entirely but not validation on submission.

@Chachaproper
Copy link
Contributor

I can do PR.

Prop noValidate override liveValidate and disable validation onSubmit. Are there any pitfalls?

@n1k0
Copy link
Collaborator

n1k0 commented Jun 20, 2016

Would be great to have a PR, thanks! A noValidate attribute sounds good. Should be trivial to implement (famous last words).

@frwickst
Copy link

Is there any work being done for this? Is it really so that there is currently no possible way to show errors that comes from a backend? So if the server responds 400 for a form requests, we just need to handle it some other way?

This seem like a gaping hole in the functionality of this project if the above statement is correct. Handling errors that comes from the backend would be great since then the backend can take care of translations and message text while the front-end (react) can keep to the view/rendering part.

But I will be happily proven wrong in that react-jsonschema-form does not support external errors to be injected in the form.

@nuclearspike
Copy link
Author

nuclearspike commented Feb 21, 2017 via email

@frwickst
Copy link

Do you have any examples of the how you are doing it right now? If not it is ok, just wondering if there would be even some way of inserting error messages without needing to fork the project and create some ugly hack. If I had the time I would try to do a proper fix, but right this moment I don't have that luxury.

@nuclearspike
Copy link
Author

nuclearspike commented Feb 21, 2017 via email

@frwickst
Copy link

Alright, thanks for the information. I might try that, but as you say it is a bit hacky :) Hopefully full support for external errors will be introduced at some point as this project looks really nice otherwise and uses a proper standard for structuring the JSON, but the lack of external errors is currently keeping me back from using it.

@glasserc glasserc changed the title RegEx validation errors need a way to define error text. Allow the insertion of errors from outside JSONSchema validation Feb 24, 2017
@glasserc
Copy link
Contributor

@frwickst No, as far as I know nobody is working on this. It hasn't been relevant to our use case. Patches welcome :)

@frwickst
Copy link

@glasserc : How do you handle events where the server does not return a successful response? Say that a server responds with an error message saying that some part of the form is not correct? Do you do create a global toast/notification in those cases or do you have any other solutions?

@n1k0
Copy link
Collaborator

n1k0 commented Feb 24, 2017

Would async validation solve your issue? It's been stalled forever in #198, so anybody wanting to take over it is more than welcome.

@glasserc
Copy link
Contributor

@frwickst The projects where we use react-jsonschema-form just use the same JSON schema to validate on the server side as well as on the browser, so we never have this kind of error message. Any other error messages (for instance, server not available) aren't specific to the form and don't need to be mingled with the errors of the form. That's why I said this kind of error isn't really relevant to our use case. For us, react-jsonschema-form is really just a JSONSchema form, although I understand that many people are using it as a generic form construction library.

@galiai
Copy link

galiai commented Mar 9, 2017

@nuclearspike Will appreciate if you can share some of your code on how you handle server errors.

@nuclearspike
Copy link
Author

nuclearspike commented Mar 9, 2017

@galiai

.catch(({ response, json }) => {
        if (response) {
          switch (response.status) {
            case 422:
              const toDisplay = json.errors ? json.errors : json;
              //errors is output in an alert in a bullet list above the form.
              const errors = Object.keys(toDisplay).map(key => `${prettifyCamelCase(key)} ${toDisplay[key].join(', ')}`);
             
              this.setState({errors});
              break;
           [....other handlers]
            default:
              this.setState({errorMessage: `An unhandled error has occurred. ${response.status}: ${response.statusText}`});
          }
        }

I used to have it set the state of the schema form component but that trick stopped working at some point. Now I just output the 422 errors above the form.

@baohouse
Copy link

baohouse commented Aug 18, 2017

One of the techniques I'm currently using is to have a ref on the submit button to manually invoke the form validation (it's documented on the README's Tips & Tricks section as external form control https://jsfiddle.net/spacebaboon/g5a1re63/). So the idea is that when this.state.errors (errors from the server) has changed, I am triggering the form validation (by programatically clicking the button). The second half of the technique is to inject errors into the form, which I use the custom validation function passed in via the 'validate' prop. However, in order to allow the user to press submit again to make the server call, I have to prevent the merging of the server error into the custom validation (which right now, I do that by clearing this.state.errors).

function injectServerResponse(response) {
  this.setState({ serverResponse: response });
  this.submitButton.click(); // Triggers form validation
  this.setState({ serverResponse: null }); // This allows user to continue to submit
}

function customValidation(formData, errors) {
  const { serverResponse } = this.state;
  if (_.isObject(serverResponse)) {
    if (serverResponse.details) {
      errors.addError(serverResponse.details);
    }
    if (_.isObject(serverResponse.validation)) {
      _.each(serverResponse.validation.fieldValidationErrors, (error) => {
        if (errors[error.fieldName] && error.errorDescription) {
          errors[error.fieldName].addError(error.errorDescription);
        }
      });
    }
  }
  return errors;
}

I don't really like this solution because it relies on having the form state unsynchronized from the parent component's state.

I found what looks to be a better example of triggering validation from #197 (comment) by extending the Form class and calling this.validate directly.

@herzaso
Copy link

herzaso commented Aug 31, 2017

The whole point of creating a json schema is to be able to define a generic form.
Relating to content of the schema from the code should be enabled as a last resort, but IMO, there should be a more generic approach to this problem - meaning that the error strings should be defined in the json file itself, rather than a function in the code (which couples the code with the schema)

@mavarazy
Copy link

As an alternative solution, can there be an approach similar to what redux-form does with SubmissionError https://redux-form.com/7.1.1/docs/api/submissionerror.md/

So if onSubmit is a function that returns a Promise, then it can throw SubmissionError, that will be translated into errorSchema and global errors accordingly. That way users won't need to manage additional properties for the form and all they would need to do is translate server errors in appropriate format.

I can do the PR for that.

@davidbarratt
Copy link

uhh.. I think it would be best if the Form component had a errorSchema prop. The error list would be generated from the schema and the changes (like all state) would come through the onChange and onSubmit callbacks. This would allow you to control the errorSchema like you can the other schemas / formData.

@gnimmelf
Copy link

gnimmelf commented Mar 18, 2018

@davidbarratt
Copy link

Try out #874... still needs docs and tests.

@keul
Copy link
Contributor

keul commented Apr 18, 2018

Found this issue too late. Hope to see #874 merged soon!
Meanwhile I moved a promise based approach to a small package: https://github.com/bopen/react-jsonschema-form-async
It works for my usecase.

@epicfaace
Copy link
Member

Continue discussion on this wrapper issue: #1389

James-Brown98 pushed a commit to James-Brown98/react-jsonschema-form that referenced this issue Feb 24, 2022
…ts (rjsf-team#155)

Alter the RJSFB library to allow the setting of ui:placeholder elements
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.