Vee-validate: Repeater/array minimum length validator?

Created on 17 Apr 2017  Â·  3Comments  Â·  Source: logaretm/vee-validate

If I have a repeater field (an array of inputs via v-for) and I have the ability to add (push) or remove (splice) inputs from this repeater, is there a validation rule to specify that list of inputs need to be a minimum size? i.e. "there needs to be at least 1 person in the list of people (where each person is a set of fields in a v-for)"

For example with the Vuelidate plugin you can specify a "minLength" for an array (collection) length/size: https://monterail.github.io/vuelidate/#sub-collections-validation

✨ enhancement

Most helpful comment

I see, currently the validator does not handle arrays, for multi-valued inputs it just validates each one's value, I will need to think of a good way to handle it without introducing extra rules.

although there is a workaround, you can have a computed attribute that returns the array length, and validate that computed attribute instead using directive arg:

<template>
  <div>
    <input type="hidden" v-validate:length="'min_value:3'" name="person_list">
  </div>
</template>

<script>
  export default {
    // stuff ...
    computed: {
      length() {
        return this.list.length;
      }  
    }

  }
</script>

although this is not straight forward to use, but it would work at the moment, vuelidate handles it pretty well because they have different ideology when it comes to validating data, vee-validate however validates inputs, so it kinda comes short when it comes to similar scenarios. I will try to support both ideologies after the main release.

All 3 comments

I see, currently the validator does not handle arrays, for multi-valued inputs it just validates each one's value, I will need to think of a good way to handle it without introducing extra rules.

although there is a workaround, you can have a computed attribute that returns the array length, and validate that computed attribute instead using directive arg:

<template>
  <div>
    <input type="hidden" v-validate:length="'min_value:3'" name="person_list">
  </div>
</template>

<script>
  export default {
    // stuff ...
    computed: {
      length() {
        return this.list.length;
      }  
    }

  }
</script>

although this is not straight forward to use, but it would work at the moment, vuelidate handles it pretty well because they have different ideology when it comes to validating data, vee-validate however validates inputs, so it kinda comes short when it comes to similar scenarios. I will try to support both ideologies after the main release.

Thanks for the workaround. I did something similar where I show the submit button using fields.passed() && list.length > 0

Here are a couple custom validators I just made. I'm using vue-select which can add tags, so I need to validate mintags:3 and maxtags:10.

I left my console.log()s in the validators so you can run them and take a look at the kind of data flowing through.

You can ignore the validationMessages parts; I left those in there to show you a decent way to pull them into your project. Those have the following form:

const dictionary = {
    custom: {
        amount: {
            required: () => 'Amount is required',
        },
        dog_meat: {
            required: () => 'Dog meat is required!',
            min: () => 'Meat of dog must be at least 3 characters',
        },
    },
},

export default dictionary;

app.js

import { maxTagsRule, minTagsRule } from './validators';
import validationMessages from './validationMessages';

const app = new Vue({
    el: '#app',

    created() {
        // load in the default validation error messages
        this.$validator.localize('en', validationMessages);

        // load in the custom form input validators
        this.$validator.extend('maxtags', maxTagsRule);
        this.$validator.extend('mintags', minTagsRule);
    },
});

validators.js

export const maxTagsRule = {
    getMessage(field, maxCount, data) {
        // field maps to <input name="field">
        console.log('field', field);
        console.log('maxCount', maxCount[0]);
        console.log('data', data);
        return (data && data.message) || `Number of ${field} must be ${maxCount[0]} or less`;
    },
    validate(tags, maxCount) {
        console.log('tags', tags, 'maxCount', maxCount[0]);
        if (tags.length <= maxCount[0]) {
            console.log(`max ${maxCount[0]}, actual ${tags.length}`);
            return true;
        }
        return false;
    },
};

export const minTagsRule = {
    getMessage(field, minCount, data) {
        // field maps to <input name="field">
        console.log('field', field);
        console.log('minCount', minCount[0]);
        console.log('data', data);
        return (data && data.message) || `Number of ${field} must be ${minCount[0]} or more`;
    },
    validate(tags, minCount) {
        console.log('tags', tags, 'minCount', minCount[0]);
        if (tags.length >= minCount[0]) {
            console.log(`max ${minCount[0]}, actual ${tags.length}`);
            return true;
        }
        return false;
    },
};

usage:

    v-validate="'required|mintags:2|maxtags:3'"
    name="categories"

Note that validate() either returns true or false. You should be able to use my samples here to do any wild validations you like. Good luck and have fun.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

schel4ok picture schel4ok  Â·  3Comments

ash0080 picture ash0080  Â·  3Comments

Hoagiex picture Hoagiex  Â·  3Comments

yyyuuu777 picture yyyuuu777  Â·  3Comments

triffer picture triffer  Â·  3Comments