wearebraid / vue-formulate

⚡️ The easiest way to build forms with Vue.
https://vueformulate.com
MIT License
2.24k stars 246 forks source link

Unable to get form errors from inside nested form group #491

Open henryaj opened 2 years ago

henryaj commented 2 years ago

If you have a form group with nested form fields, how do you get the errors on those form fields?

If I bind a handler to the parent form, like this:

<formulate-input @validation="myValidationEvent"> ...

If I log the output from that event to the console, the output for a normal form field looks this this:

{name: 'personalDetails', errors: Array(1), hasErrors: true}

where errors is an array containing the errors. But if I do the same for a form group, I get this:

{name: 'personalDetails', errors: Array(0), hasErrors: true}

Note the empty errors array – this seems like a bug. How do I get to the errors on the form fields nested inside the group?

justin-schroeder commented 2 years ago

Generally I discourage developers from trying and explicitly access the validation messages and maintain their own state — Vue Formulate is built to handle just that problem, but in a few cases people have needed to revert to using the validation event as an escape hatch for some side effects they want to run. So my first question is — do you really need those messages?

In regards to your specific question — where did you put the validation listener — on the <FormulateInput type="group">? Keep in mind that the group input is an input itself that can have it's own validation rules — so you don't automatically get inherited values from children of that group — you can always bind the @validation event on children of a group explicitly too — but again, sorting out the state on all that stuff gets kinda complicated — the juice might not be worth the squeeze.

As an aside that is worth noting — FormKit (the next major version) has a completely different internal mechanism for working with groups that is dramatically better that the current status quo.

henryaj commented 2 years ago

Thanks for the quick reply!

Yeah, maybe it's a bit of a smell on my end. The use case is to display a single list of warnings at the very end of the form, as the form we have is very long and complex, so a summary of any missing fields/validation warnings is useful. Is there a way of doing this that doesn't involve me directly grabbing the errors in this way?

The listener is on the parent form that the form group is inside. I get the same result with the listener on the form group itself. Am experimenting with putting the listener on the form group's inputs directly, but this seems bad.

justin-schroeder commented 2 years ago

There's an easy to use solution that gets you part of the way there, but not with the specific validation messages. If you put the <FormulateErrors /> component near the bottom of your form (I usually do it right above the submit button) you can add a message to display when any errors are detected in the form and someone tries to submit. This is for this exact use case (long forms), but it falls short of actually delivering the specific messages that are failing. Partly this is by design since validation messages are written to be displayed in context. For example a validation message might something like "this field is invalid" which is unhelpful when it's displayed in a rollup at the bottom of the screen.

If you really need to show the specific messages though, you'll need to do some work like binding to the @validation handler or using a ref to get a hold of the context object explicitly to access them.

justin-schroeder commented 2 years ago

FYI it's the invalid-message prop on the form: https://vueformulate.com/guide/forms/#form-validation

henryaj commented 2 years ago

Thanks so much for your help with this, Justin! Happy to close this out (or can leave it open if you think this is a genuine bug).

JackEdwardLyons commented 2 years ago

@henryaj I had this same issue (I need to display a list of all errors in the form). The solutions provided above just don't work well for grouped data.

I created a custom component that loops over the failingFields data of the form to create a list of errors. To get the form errors data, I created a simple watcher like this:

mounted () {
  this.$watch(
    () => (this.$refs[this.formId] as any).failingFields, // add a ref to your form 
      (value: Record<string, any>) => {
        this.failingFields = value // create a failingFields object in data()
    }
  }
)

You can then use the failingFields data to help list out the errors. Obviously your use case will need more work on top, but hopefully, this helps. Let me know if you need more info