Issaki
Issaki

Reputation: 1064

How to properly add custom validation to array in vuelidate

I have an array of objects with the following structure

varientSections: [
    {
      type: "",
      values: [
        {
          varientId: 0,
          individualValue: ""
        }
      ]
    }
  ]

I created a custom validation called isDuplicate, which checks for duplicate value for the property "type". For example

varientSections: [
    {
      type: "Basket",
      values: [
        {
          varientId: 0,
          individualValue: ""
        }
      ]
    },
    {
      type: "Basket", // ERROR: Duplicate with the "above" object
      values: [
        {
          varientId: 1,
          individualValue: ""
        }
      ]
    }
  ],

I was able to get my custom validation to work. However, the $invalid property will be false for all the objects present in the array. Hence, all the objects in the array will be highlighted in red

enter image description here

Below is my validation code:

validations: {
varientSections: {
  $each: {
    type: {
      required,
      isDuplicate(type, varient) {
        console.log(varient);
        const varientIndex = this.varientSections.findIndex(
          v => v.type === type
        );

        var isWrong = true;
        this.varientSections.forEach((varObject, index) => {
          if (index !== varientIndex) {
            if (varObject.type === varient.type) {
              isWrong = false;
            }
          }
        });

        return isWrong;
      }
    },
    values: {
      $each: {
        individualValue: {
          required
        }
      }
    }
  }
}
},

Upvotes: 7

Views: 20404

Answers (4)

Manuchehr
Manuchehr

Reputation: 71

In vue3 composable you should use helpers.forEach function:

import { useVuelidate } from "@vuelidate/core";  
import { helpers, required } from "@vuelidate/validators";

const form = reactive({
  users: [
    {
      name: "John"
      value: "Doe"
    }
  ]
})

const rules = {
  users: {
    $each: helpers.forEach({
      name: { required },
      value: { required }
    })
  }
}

const $v = useVuelidate(rules, form)

Upvotes: 2

Amir
Amir

Reputation: 314

this is worked for me

 <b-row v-for="(field,index) in fields" :key="index">
   <b-colxx lg="6">
     <b-form-group :label="$t('target.name')">
       <b-form-input v-model="field.name" :state="!$v.fields.$each[index].name.$error"/>
       <b-form-invalid-feedback v-if="!$v.fields.$each[index].name.required">name is    required</b-form-invalid-feedback>


     </b-form-group>

   </b-colxx>
   <b-colxx lg="6">
     <b-form-group :label="$t('target.value')">
       <b-form-input v-model="field.value" :state="!$v.fields.$each[index].value.$error"/>
       <b-form-invalid-feedback v-if="!$v.fields.$each[index].value.required">value is    required</b-form-invalid-feedback>
     </b-form-group>
   </b-colxx>
 </b-row> 
 

.

 data() { 
      return {
          fields: [
                    {name: null, value: null},
                    {name: null, value: null} ]  
              } 
           }, 

.

validations: {
          fields: {
           $each: {
             name: {
               required
             },
             value: {
               required
             }
           }
   
   
       },
     },

Upvotes: 0

Aner
Aner

Reputation: 560

I had the exact same need and found that the solution was quite simple once you wrap your head around what you're trying to do. Your validator needs to trigger only if the current item is a duplicate of any previous items.

Something like this:

validations: {
  varientSections: {
    $each: {
      isUnique(currItem, itemArr) {
        // Find the index of the first item in the array that has the same type
        var firstIdx = itemArr.findIndex((item /*, index, arr*/) =>  currItem.type === item.type );
        // If it's the same as the current item then it is not a duplicte
        if(currItem === itemArr[firstIdx])
          return true;
        // All others are considered duplicates
        return false;
      },
      type: { required }
    }
  }
}

Upvotes: 0

Christhofer Natalius
Christhofer Natalius

Reputation: 3388

Should be something like this.

<div v-for="(vs, index) in varientSections" :key="index">
    <input :class="{'is-error': $v.varientSections.$each[index].type.$error}" type="text" v-model="vs.type">
    <input :class="{'is-error': $v.varientSections.$each[index].value.$error}" type="text" v-model="vs.value>
</div>

Change the error class to fit your need.

Upvotes: 6

Related Questions