Vee-validate: Validation: at least one checkbox is checked

Created on 25 Oct 2018  ·  6Comments  ·  Source: logaretm/vee-validate

_I understand that this is not really a feature request nor a bug, but I don't know where I could ask for some information :S_

Is your feature request related to a problem? Please describe.
Well my problem is that I would like to build a custom validator that validates if at least one of my checkboxes is checked. I realize that this subject was discussed before, but somehow I don't fully understand.

I found the following links:
https://jsfiddle.net/awolf2904/pndvq438/
https://codereview.stackexchange.com/questions/151988/custom-checkbox-validation-on-submit

Which seem to discuss this matter.

What I don't understand:

  • _Why does the validation has to be evoked manually? Why is it not just validated on user change on the checkboxes?_ – In this matter I also found this: Changing events per field where I would assume that the validation should be executed on each change of the input value.
  • Also I don't understand how the validateAll is actually used. How should fields be passed into it? I always cause the script to break, because of endless loops.

I think some of my code could describe my problem better:

<template>
…
<div v-for="(list, index) in mailChimpLists" :key="index">
  <input
    :id="`NewsletterForm__list-id_${index}`"
    v-model="list.checked"
    v-validate ="'atLeastOne:list.checked'"
    data-vv-validate-on="change"
    type="checkbox"
    name="interest"
    :value="list.list_label"
  >
  <label :for="`NewsletterForm__list-id_${index}`">{{ list.list_label }}</label>
</div>
…
</template>

… 
  mounted() {
    Validator.extend('atLeastOne', {
      getMessage: field => 'at least one ' + field + ' has to be checked',
      validate: value => {
        this.log('value: ', value)
        return value || this.mailChimpLists.some(list => list.checked) // check if at least one is checked
      }
    })
  }

Whereas my data within mailChimpLists look like this:

mailChimpLists: [
  {
    checked:true
    list_label: "List 1",
    subscription_link: "https://…"
  },
  {
    list_label: "List 2",
    subscription_link: "https://wea…"
  }
]

Should my custom rule not return false if some() does not find any checked boxes?
And this should then trigger an error?

Or is the validation not executed if there is no checkbox selected anymore?

Describe the solution you'd like
In addition to get help with the above mentioned issue I would assume that susch a validation is needed quite often and could maybe become one of the standard validation rules?

I would be extremely grateful if someone could help me out with this and give me a little hint.
Cheers

J

❔ question

Most helpful comment

Here is a small example to address your issue:

https://jsfiddle.net/logaretm/vxg5ydm2/11/

The lists and the model that tracks which is selected is not the same, this is because v-validate directive does not work properly with v-for loops which is a limitation for using directives in Vue (directive does not have access to the loop context). Our rule is very simple, it just checks if the value passed has a minimum length of x. It is considered pure.

Validation isn't invoked manually, it is indeed invoked whenever the user interacts with the input, for checkboxes the default event is change. The validate or validateAll are only used on form submission to ensure inputs were not manipulated in other ways, for example, I can just open the dev tools and manipulate the input value without emitting events or even updating the v-model (all the more reason to have server-side validation). VeeValidate knows how to resolve the current values for those inputs (they are bound by a directive after all), so it can reliably validate them without having you to pass the values.

Here is an example that is using your approach, even with the v-for limitation:

https://jsfiddle.net/logaretm/t81d32ec/6/

Notice that we added the continues modifier since the required and atLeastOne rules are basically equivalent, so we would like to forcibly use our validator even if the field isn't required. The modifier is documented here.

All 6 comments

I'm having kind of the same problem. I have two checkboxes and at least one of them has to be checked or all checked is also possible.

My custom validator triggers every time i check/uncheck expect if only one checkbox is checked and gets unchecked, then the validation isn't triggered.
I also tried playing around with the Changing Events Per Field to no avail.

So my question is, how do i achieve to trigger my custom validator if the last checked checkbox gets unchecked.

My custom validator triggers every time i check/uncheck expect if only one checkbox is checked and gets unchecked, then the validation isn't triggered.

That's exactly what happens to me. As soon as all fields are empty the validator quits his work..

I added a watcher, which makes a deep check if my objects are changing (also registering the uncheck moment of the last checkbox)

  watch: {
    // whenever my lists change, this function will run
    mailChimpLists: {
      handler: function() {

        this.mailChimpLists.forEach(list => {
          this.$validator.validate('interest', list.checked)
        })
      },
      deep: true
    }
  },

My validator looks like this:

Validator.extend('atLeastOne', {
  getMessage: field => 'at least one ' + field + ' has to be checked.',
  validate: value => {
    this.log('value: ', value)
    const validation = this.mailChimpLists.some(list => list.checked)
    this.log('validation: ', validation)
    return validation
  }
})

If I log my validation within the validator, I can clearly see that it evaluates to false if I uncheck my last checkbox.
Also if I inspect my components data with Vue dev tools I see that there is an error listed (the one concerning my checkboxes).

But somehow this is not displayed in my error messages below.

            <p v-if="errors" class="NewsletterForm__error">
              {{ errors }}
            </p>

It only appears on screen If I enter a wrong email and the validator is run because of that. This causes a change to errors and triggers display on screen...

I must be missing something completely...

Here is a small example to address your issue:

https://jsfiddle.net/logaretm/vxg5ydm2/11/

The lists and the model that tracks which is selected is not the same, this is because v-validate directive does not work properly with v-for loops which is a limitation for using directives in Vue (directive does not have access to the loop context). Our rule is very simple, it just checks if the value passed has a minimum length of x. It is considered pure.

Validation isn't invoked manually, it is indeed invoked whenever the user interacts with the input, for checkboxes the default event is change. The validate or validateAll are only used on form submission to ensure inputs were not manipulated in other ways, for example, I can just open the dev tools and manipulate the input value without emitting events or even updating the v-model (all the more reason to have server-side validation). VeeValidate knows how to resolve the current values for those inputs (they are bound by a directive after all), so it can reliably validate them without having you to pass the values.

Here is an example that is using your approach, even with the v-for limitation:

https://jsfiddle.net/logaretm/t81d32ec/6/

Notice that we added the continues modifier since the required and atLeastOne rules are basically equivalent, so we would like to forcibly use our validator even if the field isn't required. The modifier is documented here.

Thank you so much.
I did not know the continues modifier! And I also did not realize that required can work for a set of checkboxes. This would have saved some time for me.
I like your example though.
And thank you so much for the work to write those examples. Much appreciated!

No problem, happy to help. Being the author of this library it makes it hard to write proper docs for it since I know most nooks and crannies of it.

So feel free to suggest improvements to the docs!

Was this page helpful?
0 / 5 - 0 ratings