What is the best way to implement vuelidate when dealing with an array of objects?

In my form questionnaire, I am looping through an array of objects. Each object has five properties, but only one property needs validation. The validation setup in the component looks like this:

specificGifts: {
      $each: {
        passThrough: {
          required: requiredIf(function(value) {
            return this.docs.includes('Will')
          })
        }
      }
    },

After reading the vuelidate documents, I realized that instead of the code snippet below in my form html:

<div
              v-for="(gift, i) in specificGifts"
              :key="i"
            >
<v-select
                label="How does this specific gift pass if the recipient does not survive?"
                v-model="gift.passThrough"
                :items="specificGiftPassThrough"
              ></v-select>
    </div>

I should be using:

<div
              v-for="(gift, i) in $v.specificGifts.$each.$iter"
              :key="i"
            >
<v-select
                label="How does this specific gift pass if the recipient does not survive?"
                v-model="gift.passThrough.$model"
                :items="specificGiftPassThrough"
              ></v-select>
    </div>

The data section of my vuejs component is as follows:

data(){
return{
specificGifts: []
}
}

However, when running the code, I encounter the error "Cannot read property $model of undefined". Even after debugging by checking $v.specificGifts.$each.$iter, I still face more console errors. Can anyone provide insight into what could be going wrong? Is there a better approach to handling validation without having to manually loop through a $v property just for vuelidate?

Answer №1

Dealing with survey validation has been a recent challenge for me, and I'm excited to share my solution.

In my scenario, I have numerous questions, each with multiple answers represented by radio buttons. My goal is to ensure that every question has at least one radio button checked for validation.

To begin, I retrieved an array of question objects from the API (note that the "value" properties may vary):

[
     {
        "value":"a",
        "name":"first question",
        "answers":[
           {
              "label":"first answer",
              "value":"a1"
           },
           {
              "label":"second answer",
              "value":"a2"
           },
           ...
        ]
     },
     {
        "value":"b",
        "name":"second question",
        "answers":[
           {
              "label":"first answer",
              "value":"b1"
           },
           {
              "label":"second answer",
              "value":"b2"
           },
           ...
        ]
     }
     ...
]

After receiving the API response, I created another array of objects specifically for the answers:

this.questions.map(function (item) {
  return {
    questionId: item.value,
    answerId: null
  };
})

The resulting structure looks like this:

[
    {
        questionId: 'a',
        answerId: null,
    },
    {
        questionId: 'b',
        answerId: null,
    }
    ...
]

I utilized the Quasar framework for building the template, but you can achieve similar functionality with basic HTML:

  <q-field v-for="(question, key) in questions" :key="question.value"
    filled
    :label="question.name"
  >
    <template v-slot:control>
      <q-option-group
        v-model="answers[key].answerId"
        :options="question.answers"
        type="radio"
      />
    </template>
  </q-field>

Lastly, my validation rule is set as follows:

validations: {
  answers: {
    $each: {
      answerId: {
        required
      }
    }
  }
}

Answer №2

It's worth mentioning that in the latest version of Vuelidate (Vuelidate 3), the $each helper parameter mentioned in a previous answer has been eliminated.

Instead, users are now encouraged to create a nested Vue component to handle each object in an array and utilize nested validations.

The example provided in the link above will be displayed here in case it gets removed from their documentation in the future.

Wrapper Component:

<!-- PeopleList.vue -->
<template>
  <div>
    <PersonInput
      v-for="(person, index) of people"
      :person="person"
      :key="index"
      @updatePerson="person = $event"
    />
    <!-- This list will include all errors,
         both from this component and errors from every <PersonInput> -->
    <div v-for="error of v$.$errors" :key="error.$uid">
      {{ error.$message }}
    </div>
  </div>
</template>

<script>
import { useVuelidate } from '@vuelidate/core'
import PersonInput from '@/components/PersonInput'

export default {
  components: { PersonInput },
  setup () {
    return { v$: useVuelidate() }
  },
  data() {
    return {
      people: [ { name: 'John' }, { name: '' } ]
    }
  },
  validations: {
    people: {
      required,
      minLength: minLength(3),
    }
  }
}
</script>

Nested component:

<!-- PersonInput.vue -->
<template>
  <div>
    <input type="text" :value="person.name" @input="$emit('updatePerson', { name: $event })" />
  </div>
</template>

<script>
import { useVuelidate } from '@vuelidate/core'

export default {
  props: {
    person: { type: Object, required: true },
  },
  setup () {
    return { v$: useVuelidate() }
  },
  validations: {
    person: {
      required,
      minLength: minLength(2),
    }
  }
}
</script>

Answer №3

I have encountered significant difficulties with Vuelidate as well. My suggestion to resolve this issue is to switch from Vuelidate to VeeValidate.

The transition is not complex and will ultimately save you a substantial amount of time.

VeeValidate provides straightforward options for validation, such as custom messages for each rule (eliminating the need for computed properties to display error messages) and the ability to apply rules based on specific conditions.

You can access the documentation here

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Ionic timer binding issue: troubleshooting tips

Recently, I developed a stopwatch factory service that primarily focuses on running. Please disregard the reset and other functionalities as they are not yet implemented. Despite setting up $scope.time to capture timer changes, it doesn't seem to upd ...

The page refreshes automatically when the search icon is clicked, but the ajax method does not trigger the page reload

Whenever I click on the search-box <a>, the JavaScript is triggered but doesn't execute the ajax method filter_data_guide_specs(). Instead, the page automatically reloads and fails to read the JS code. HTML <div class="form-group"> < ...

Restrictions of Vue Data Objects

I'm currently pondering the optimal amount of data to store in a Vue data object. Imagine I have over 4,000 objects structured like this: personWithAppointment = { id: 1, appointment_id: 1, first_name: 'Jim', last_name: 'Jim&ap ...

How can a CSS class be used to toggle the visibility of a DIV element with JavaScript?

I've been researching and came across several scripts that allow for toggling the contents of a DIV by clicking on a button, however, they all use IDs. What I am looking to achieve is similar but using classes instead of IDs. This way, if I want to h ...

How to generate a list in Vue.js using an array

I am facing an unusual issue while trying to create a list using Vuejs. Here is the structure of my array: const sr = new Vue({ el: '#singlerecipe-app', data: { checkeditems: [], }, }); This array is initialized in my HTML as follows: ...

An error occurs when using e.slice function

I am facing an issue with my kendoDropDownList that uses kendo UI and jQuery. I cannot figure out why this error is occurring. $("#drpState").kendoDropDownList({ optionLabel: "States...", delay: 10, ...

The error message "writeUserData is not defined" is indicating that there is an undefined variable in the react code

I'm still learning React and I've been working on a new function: This is my code in summary: class Signup extends Component { constructor(props) { super(props); } componentDidMount() { } writeUserData = (userId, username, email) => ...

An exploration of the functionality of the String.fromCharCode method when used with a mobile keyboard in an HTML input

I'm looking to retrieve the keyboard key code and translate it into characters. I've been utilizing the String.fromCharCode javascript method, but it seems to only be effective with a computer keyboard and not functioning properly with a mobile k ...

What is the best way to toggle dropdown menu items using jQuery?

Upon clicking on an li, the dropdown menu associated with it slides down. If another adjacent li is clicked, its drop down menu will slide down while the previous one slides back up. However, if I click on the same li to open and close it, the drop down m ...

What steps should be taken for the authentication process when using Active Directory (with LDAP) with an AngularJS/JavaScript frontend?

Currently, I am tackling a project that involves authenticating users in an application using their Windows credentials. The frontend is built with AngularJS and the backend with Java. After conducting extensive research, I came to the realization that it ...

What could be the reason for ng-init failing to activate the $watch listener?

When I run the code in my application, there are instances where ng-init fails to trigger the $watch function. Any suggestions on how to fix this? HTML <div ng-controller="MyCtrl"> <p ng-init="stages = 'coco'">{{x}}</p> < ...

I am unable to pass a variable through a callback, and I cannot assign a promise to a

Currently, I am facing a challenge with my code where I need to loop through a hard-coded data set to determine the distance from a user-entered location using Google's web API. The issue lies in passing an ID variable down through the code so that I ...

Learn the process of developing a web client application using Node.js and NPM similar to the AngularJS tutorial

I am new to nodejs, npm and angularjs. I recently explored the angularjs tutorial project available at https://github.com/angular/angular-phonecat.git. This project has been really interesting for me as it demonstrates how easy it is to manage modules wi ...

The local variable within the Angular constructor is not initialized until the ngOnInit() function is invoked

I am encountering difficulties with making backend calls from Angular. In my component, I am fetching the "category" parameter from the URL as shown below: export class ProductsComponent{ productList = [] category = "" $params; $products ...

Utilizing the @property decorator within Vue components

I'm currently working on a project using Vue.js and came across something new to me - @property etc. Could someone please explain what this means? Despite searching globally, I couldn't find any references in my project folder, so I'm unsur ...

The Echart bar graph is not displaying when trying to use JSON data

Seeking assistance as a beginner in building Basic Bar inverted axes using json data. I am trying to achieve a chart similar to Bar Inverted Axes, but encountering issues with the chart not displaying properly. Utilizing Angular to develop the web applicat ...

Display the X button solely once text has been inputted into the textbox

I have integrated a text clearing plugin from here in my project to allow users to clear the input field by clicking on an X button. However, I want to modify the code so that the X button only appears when there is text entered in the textbox. I attempt ...

Working with handleChange and onSubmit functions in pure JavaScript without any libraries

Currently developing my initial app, which is a login/register form using JS/Node.js/MySQL. I am facing issues with connecting my form to the database in order to store user data. I haven't utilized "handleChange" or "onSubmit" functions as I am not e ...

Upgrading React Native hot reloading leads to a complete reload of the application

Recently, I updated my React Native app from version 0.48 to 0.55. Unfortunately, after the upgrade, hot reloading stopped functioning. Any modifications to the files now trigger a complete reload of the app. I have attempted clearing all cache related to ...

Vue-Router: Moving to a nested state or route based on their name and parameters

How do I use $router.push to navigate to a child state? This is my current route configuration: const routes = [ { path: "/customers", name: 'Customers', components: {content: CustomersMain}, props: {header: true, content: false}, ...