How to properly add custom validation to array in vuelidate
Asked Answered
H

4

7

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
        }
      }
    }
  }
}
},
Hibben answered 9/7, 2019 at 2:20 Comment(3)
use $each[index] in templateOsgood
Hi @ChristhoferNatalius thanks for the reply! What do you mean by that?Hibben
I found the official guide here: vuelidate.js.org/#sub-collections-validation . Should be suitable for your case, have a look first.Alienable
O
6

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.

Osgood answered 9/7, 2019 at 2:26 Comment(0)
D
1

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)
Destinydestitute answered 22/1 at 13:12 Comment(1)
Welcome to Stack Overflow! Thank you for your answer. Please provide more details about your solution. Code snippets, high quality descriptions, or any relevant information would be great. Clear and concise answers are more helpful and easier to understand for everyone. Edit your answer with specifics to raise the quality of your answer. For more information: How To: Write good answers. Happy coding!Diagenesis
I
0

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 }
    }
  }
}
Inboard answered 15/7, 2020 at 19:40 Comment(0)
D
0

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
             }
           }
   
   
       },
     },
Dredger answered 22/5, 2022 at 20:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.