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

Aggregated form validation #4181

Closed
okoell opened this issue Mar 9, 2023 · 10 comments · Fixed by #4553 · May be fixed by #4554
Closed

Aggregated form validation #4181

okoell opened this issue Mar 9, 2023 · 10 comments · Fixed by #4553 · May be fixed by #4554
Labels
☔ has workaround this issue has a workaround

Comments

@okoell
Copy link

okoell commented Mar 9, 2023

We are in the process of migration a large Vue 2 application to Vue 3 - including the update from vee-validate v3 to v4.

The UI provides a list of forms for a (dynamic) list of model entities, all of the same type. It can be submitted when one or more of the forms have changes and are valid.

This requires an aggregated view of the single form validation states. In vee-validate v3 we implemented this with Nested Observers - i.e. we have a global ValidationObserver combining the state of the contained form ValidationObservers.
This is documented (if not recommended) in the v3 docs and works quite nicely.

In vee-validate v4 I don't see anything comparable supporting this use case.

Do you have any plans to introduce this later? Or ideas how to tackle the problem with the existing features?

@logaretm
Copy link
Owner

The nested observers/forms idea was removed since I couldn't find a good justification for it, most single root forms can do the same thing as a nested form setup with minor changes. Having said that, I may not have encountered the use case to convince me of introducing it yet.

However, there could be a place for a simpler abstraction. Like a <FormGroup /> or something that does a similar job of useForm but minus validation and form state, only aggregates state of child fields.

I can probably start working on it for 4.9 after I figure out the details, but you can do it roughly in userland by having your own custom components that can wrap this behavior with something like this:

// FormGroup.vue
const fields = ref<FieldContext[]>([]);
provide('FormGroup', fields);


// You can build other stuff similar to this
const meta = computed(() => {
  const valid = fields.value.every(f => f.meta.valid);
  const dirty = fields.value.some(f => f.meta.dirty);
  const touched = fields.value.some(f => f.meta.touched);

  return { valid, dirty, touched };
});

// InputText.vue
const field = useField(...);
const group = inject('FormGroup');
if (group) {
  group.push(field);
}

@logaretm logaretm added the ☔ has workaround this issue has a workaround label Mar 10, 2023
@okoell
Copy link
Author

okoell commented Mar 10, 2023

Thanks a lot for your reply.

I certainly would appreciate having this feature added.

Our use case in some more detail (to perhaps help with the convincing :):

  • we have a page with multiple collapseble forms each using the same format/fields. It is intended to edit one or more items, all belonging together, in one go.
  • the page has to keep track of the aggregated form states (mostly the dirty flags) to enable/disable a global submit button if anything has changed.
  • we have a custom submit handler that goes over all nested dirty forms and performs the validation for them. If the validation passes, only the changed forms are gathered and submitted to the backend.

So, the formGroup would need to have a FormMeta state, aggregated from the contained forms in the same way as forms aggregate field meta states. Plus access to the contained forms, i.e. the values, validate and reset functions etc.

In the meantime I will try to implement this using your suggestions, but instead of registering FieldContexts on the FormGroup, I will register the contained FormContexts.

@scvnc
Copy link

scvnc commented Apr 17, 2023

I may also have near exact use case that @okoell has.. thinking about how we will have a form with some top level fields but also I want to add one to many field-identical sub-forms with at least 10 different fields and their own validation rules.

I'll study the workaround a bit but given my low experience with VeeValidate it isn't immediately obvious how it wires together. Thanks!

@kimcheung
Copy link

Yep would really be helpful if we can do this.

@Kotoriii
Copy link

As somebody currently migrating a Vue 2 to Vue 3 multi-step wizard form with multiple sub-components... Having this feature would make my life much easier. Not sure how feasible the proposed workaround would be until then

@zaalbarxx
Copy link
Contributor

Yeah, we are also trying to migrate and everything went relatively smoothly so far. But not we are kinda stuck with this. Our use case is also something like sections with validation status for each of them like:

  • Accordion -> section validation status
  • Accordion 2 -> section 2 validation status
    and so on.

The workaround I see for now is to create custom FieldGroup component which would provide custom API for "registering" fields and custom Field component which would act as decorator over original Field ( this one would inject FieldGroup's API and register itself there which would allow to aggregate the state of fields and pass them as slot.

Do you have any other idea or maybe there is some work already done on this @logaretm ?

@zaalbarxx
Copy link
Contributor

@logaretm I've done some testing, can be seen here:
https://stackblitz.com/edit/vue-3-veevalidate-form-validation-example-v2oddh?file=src%2FField.vue,src%2FApp.vue,src%2FFieldGroup.vue

Basically what is missing here for me is the access to the meta and eventually value from Field component's ref (it is not expose'd from it). Thus I cannot access the current state of the validation. My idea was to wrap the component since it contains all the logic for field management instead of creating my own version of Field with custom useField. Do you think it would be a good idea to expose additional refs from Field component ?

@zaalbarxx
Copy link
Contributor

Here is an example with modified version of vee-validate:
https://stackblitz.com/edit/vue-3-veevalidate-form-validation-example-4159qg?file=src%2Fvee-validate.esm.js,src%2Fmain.js,src%2FFieldGroup.vue,package.json,src%2FField.vue,src%2FApp.vue
As you can see in this version I exposed more refs into component public API and the feature seem to work correctly. The question is if we would be able to actually do it (maybe you see some cons of this) ?

This was referenced Nov 21, 2023
@logaretm
Copy link
Owner

Thanks for both of your contributions. I don't know about field groups since it can be user-land and I will continue the discussion there.

For the expose one, I'm fine with it as long as we get the types corrected.

@zaalbarxx
Copy link
Contributor

Sure, I'll look into that later. Regarding the groups I aso think we could expand this behavior to composable too. In that case we could create something like useFieldGroup which could be used in setup and aggregate the state of all fields ddclared in the component. The solution created here is not a breaking change in any way and obviously it would be nice for us to add it, but I'll honor your decision here.

logaretm pushed a commit that referenced this issue Nov 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
☔ has workaround this issue has a workaround
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants