pablo-abc / felte

An extensible form library for Svelte, Solid and React
https://felte.dev
MIT License
1.01k stars 44 forks source link

General Form Error #69

Closed bojanv55 closed 2 years ago

bojanv55 commented 2 years ago

I managed to implement client side validation, and also managed to parse error response from the server API and map it to the form error descriptions.

Now, there is some cases when I receive an error from server, which is not related to any of the fields. I would like to show this above all of the form fields (something like):

<h1>Form</h1>
  <div class="error">{serverError}</div>
  <form use:form>
    <label class="validation-message">Some prop:
      <input type="text" name="requiredFiveChars" required>
      <span class="error" data-felte-reporter-dom-for="requiredFiveChars"></span>
    </label>

is there already this feature in felte (something like <span class="error" data-felte-reporter-dom-for="generalErrorNotInData"></span>)? I can also set serverError proeprty in onError, but what would be the best way to clear it on any change of the form data?

pablo-abc commented 2 years ago

There is no "official" way to do something like this right now. While I understand it is a possible scenario, I haven't prioritised anything like this since it seems to be something that can be achieved outside of Felte (e.g. setting a variable from your component inside of your onError handler).

Anyway, this would (in the end) be a responsibility of the reporter. I'm not quite sure what a possible API could look like, since reporters usually expect your messages to be inside of the form element. Something like what you have outlined could be done. But it might not be a priority to implement since it would require some further consideration (and as mentioned it might be done independently). I'm open for discussing this, though!

bojanv55 commented 2 years ago

I was just trying to map spring exceptions to be shown in the GUI.

These are the methods spring provides to get

    //general error
    List<ObjectError> globalErrors = e.getBindingResult().getGlobalErrors();
    //error relate per field
    List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();

Now, I extract this to https://datatracker.ietf.org/doc/html/rfc7807 json problem response format

   {
   "type": "https://example.net/validation-error",
   "title": "Your request parameters didn't validate.", <-------------- GENERAL ERROR
   "invalid-params": [ {
                         "name": "age", <------------------------------ FIELD NAME
                         "reason": "must be a positive integer" <------ ERROR FOR THAT FIELD
                       },
                       {
                         "name": "color",
                         "reason": "must be 'green', 'red' or 'blue'"}
                     ]
   }

And using your component, I generate standard field errors as:


  <form use:form>
    <div class="validation-error" id="form-validation" data-felte-reporter-dom-for="form"></div> <---- !!!! THIS NEEDS TO BE ADDED? Something like form, or $global or $title ? this is title from json and will be "Your request parameters didn't validate."
    <div class="input-and-validation">
        <input type="text" name="age" placeholder="Age" aria-describedby="age-validation">
        <div class="validation-error" id="age-validation" data-felte-reporter-dom-for="age"></div> <-------------- FIELD ERROR from json will be "must be a positive integer"
    </div>
    <button class="button" type="submit">Send</button>
  </form>

I guess this is the part that would need to be added as some global error container? <div class="validation-error" id="form-validation" data-felte-reporter-dom-for="form"></div> <---- !!!! THIS NEEDS TO BE ADDED? Something like form, or $global or $title ? Just not sure what would be the proper naming here...

btw, I could use something like this

...
<form use:form>
    {#if globalProblem}
      <div class="validation-error">{globalProblem}</div>
    {/if}
    <div class="input-and-validation">
...

but it look ugly. comparing to one-liner <div class="validation-error" id="form-validation" data-felte-reporter-dom-for="form"></div>

bojanv55 commented 2 years ago

I actually end-up using existing functionality, just mapping the global error to new field I named "$form".

So it goes like this:

...
<form use:form>
    <div class="validation-error" id="form-validation" data-felte-reporter-dom-for="$form"></div>
...

and then in onError, I just map it to the field

export const mapToError = (response : ProblemResponse) => {
  let mapped : any = {}
  if(response.title){ //this is title from my rfc7807 JSON object
    mapped["$form"] = response.title; //$form will display errors for global form-level
  }
  if(response.invalidParams) {
    response.invalidParams.forEach(p => {
      mapped[p.name] = p.reason; //all field errors mapped as "field name" -> "error"
    });
  }
  return mapped;
}