vuelidate icon indicating copy to clipboard operation
vuelidate copied to clipboard

Dynamic collections

Open MadsBrink opened this issue 5 years ago • 11 comments

Hi

Is it possible to use collections (arrays) with a dynamic validation schema? I'm using the validation schema as a function: validations(){}

But how would i use $each in this situation?

This is a very shorten version of what I'm trying to do: https://jsfiddle.net/L7oxtj6c/9/

Thanks.

MadsBrink avatar Mar 29 '19 10:03 MadsBrink

I'm having the same issue - I would use the validations(){} format but the $each property is not an array or array-accessible inside that function.

Also I need to go 2 levels above the current object to check if a field is required.

mycarrysun avatar Apr 04 '19 15:04 mycarrysun

@MadsBrink what is wrong with

validations(){
 return {
      texts: {
      	$each:{
          text: { required },
          visible: { isBoolean }
        }
      }
    }
}

dobromir-hristov avatar May 03 '19 05:05 dobromir-hristov

Hi @dobromir-hristov

In that case, the text would still be required, even tho it's not visible. I was looking for a way to remove it from the collection if it was hidden.

MadsBrink avatar May 03 '19 07:05 MadsBrink

Then you would have to use some sort of custom validator function.

dobromir-hristov avatar May 03 '19 07:05 dobromir-hristov

If this is resolved could we close the issue?

dobromir-hristov avatar May 09 '19 18:05 dobromir-hristov

I need to be able to customize each instance of the children of the $each property. See this example:

validations() {
  let condition = true,
    vals = {
      name: { required },
      children: {
        $each: {
          name: { required }
        }
      }
    }

  this.children.forEach((child, index) => {
    if(condition){
      vals.children.$each[index].name = {}
      condition = false
    }
  })
}

This is a very dumbed down example to explain my point but if each of the children have different requirements we cannot change the requirements inside the validations function.

mycarrysun avatar May 10 '19 17:05 mycarrysun

Then you are approaching this the wrong way... $each is not meant to work that way. Restructure your data so collections with similar validation rules are kept together, under the same namespace.

dobromir-hristov avatar May 10 '19 17:05 dobromir-hristov

Approaching it the right or wrong way could be argued...but not so worried about that.

We have a very complex regulation system with each of the 50 states having different requirements of the company ordering a permit. If someone places an order with multiple states the $each property will contain let's say OH, NY, and MD permits. A parent property contains the company's information (an event will trigger an update to the parent when the company info is changed in the child), so the required information of the company may change based on which state the user chooses.

The easiest solution I can think of is just being able to go 2 parent levels up inside a requiredIf function. This is not possible though and I don't think it would make sense to make possible since you may not have a parent. I have tried to implement this myself but have not been able to access anything outside of the namespace like you mentioned.

mycarrysun avatar May 10 '19 18:05 mycarrysun

I wonder how your backend validates this 😆, the complication there is the same. You would have to instantiate different validator schemas for each State, in your collection, right? You will have to go a different route, $each wont do here. I would try to group the similarly validated fields, then you could use $each, finally merge them before sending to API? Or move the validation into each field type group, however collecting the validation results is not straight forward, as there is no standard way as of now with Vuelidate.

dobromir-hristov avatar May 10 '19 18:05 dobromir-hristov

It is a mess any way you slice the pie...lol.

Sounds good - to solve your example @MadsBrink here you go:

Vue.use(window.vuelidate.default)
const { required, requiredIf } = window.validators

new Vue({
	el: "#app",
  data: {
  	texts: [
    	{ text: '', visible: true },
      { text: '', visible: false },
			{ text: '', visible: true },
    ]
  },
  validations: {
  	texts: {
      $each: {
        text: {
          requiredIf: requiredIf(function(text){
            return !!visible
          })
        }
      }
    }
  },
})

Sorry for formatting it is copied from fiddle

mycarrysun avatar May 10 '19 21:05 mycarrysun

I'm having a similar issue, using nuxtjs.

In my case, I have a collection of objects that can increase in size, but I need to compare 2 properties in each project.

Each field is required, and field_2 needs to be greater than field_1. But how can I reach field_1 of the same level to compare?

It's not clear on the docs, and I've searched high and low about this, to no avail.

<script>
export default {
  data() {
    return {
      collection: [{
        field_1: 0,
        field_2: 0, 
      }]
    }
  },

  mixins: [validationMixin],

  validations: {
    collection: {
      $each: {
        field_1: {
          required
        },
       field_2: {
          required,
          isLarger: function(value) {
           // below is wrong.  How to reach/call the other field in the same index?
            return value > __field_1__ ? true : false 
          },
        },
      }
    }
  },

  methods: {
    addNewRow() {
      this.collection.push({
        field_1: 0,
        field_2: 0, 
      })
    }
  }
}
</script>

<template>
<div>
  <div v-for="(eachRow, index) in $v.collection.$each.$iter" :key="index">
     <input type="number" v-model.trim="eachRow.field_1.$model" placeholder="Field 1">
     <input type="number" v-model.trim="eachRow.field_2.$model" placeholder="Field 2">
  </div>

 <button type="button" @click.prevent="addNewRow()">New row</button>
</div>
</template>

urbgimtam avatar Oct 26 '21 16:10 urbgimtam