Search code examples
vue.jsvuetify.jsvuelidate

Vuelidate common validation logic


I am currently studying front-end javascript development and current doing a personal project using VueJS, Vuetify and Vuelidate.

I have a simple question.

I have two text fields as below.

  <v-layout wrap>
    <v-flex>
      <v-text-field
        label="First Name"
        maxlength="20"
        v-model.trim="firstName"
        :error-messages="firstNameErrors"
        @input="$v.firstName.$touch()"
        @blur="$v.firstName.$touch()"
      ></v-text-field>
    </v-flex>
    <v-flex>
      <v-text-field
        label="Last Name"
        v-model.trim="lastName"
        maxlength="20"
        :error-messages="lastNameErrors"
        @input="$v.lastName.$touch()"
        @blur="$v.lastName.$touch()"
      ></v-text-field>
    </v-flex>
  </v-layout>

I have a vuelidate written in the computed hook as below.

  computed: {
    firstNameErrors () {
      const errors = []
      if (!this.$v.firstName.$dirty) return errors
      !this.$v.firstName.required && errors.push('First Name is required')
      return errors
    },
    lastNameErrors () {
      const errors = []
      if (!this.$v.lastName.$dirty) return errors
      !this.$v.lastName.required && errors.push('Last Name is required')
      return errors
    }

The code works fine and does what is supposed to do, generate validation errors, "required" in my case.

My question is, I would like to include both validation error functions "firstNameErrors" and "lastNameErrors" under one function named "requiredErrors" rather than having two separate functions.

Code pen: https://codepen.io/pen/jebLgM

Any help is much appreciated.

Thank you.


Solution

  • I do not follow your logic but taking your request literally it may look like this:

     <v-layout wrap>
        <v-flex>
          <v-text-field
            label="First Name"
            maxlength="20"
            v-model.trim="firstName"
            :error-messages="requiredErrors"
            @input="$v.firstName.$touch()"
            @blur="$v.firstName.$touch()"
          ></v-text-field>
        </v-flex>
        <v-flex>
          <v-text-field
            label="Last Name"
            v-model.trim="lastName"
            maxlength="20"
            :error-messages="requiredErrors"
            @input="$v.lastName.$touch()"
            @blur="$v.lastName.$touch()"
          ></v-text-field>
        </v-flex>
      </v-layout>
    
      computed: {
        requiredErrors () {
          const errors = []
          this.$v.firstName.$dirty 
            && !this.$v.firstName.required 
            && errors.push('First Name is required')
          this.$v.lastName.$dirty
            && !this.$v.lastName.required 
            && errors.push('Last Name is required')
          return errors
        }
    

    UPDATE

    Yeah, that is why I said that I do not follow your logic (having the same message for both inputs). You can probably achieve what you want by bind-ing your function multiple times with different 1-st argument but the function can no longer be a method of your component (because those are already bound by Vue).

    Maybe something like this:

    <v-text-field
        label="First Name"
        maxlength="20"
        v-model.trim="firstName"
        :rules="requiredErrorsFirstName"
        @input="$v.firstName.$touch()"
        @blur="$v.firstName.$touch()"
      ></v-text-field>
    
    <v-text-field
        label="Last Name"
        v-model.trim="lastName"
        maxlength="20"
        :rules="requiredErrorsLastName"
        @input="$v.lastName.$touch()"
        @blur="$v.lastName.$touch()"
      ></v-text-field>
    
    computed:
    {
      requiredErrorsFirstName ()
      {
        return [requiredErrors.bind(this,'firstName')];
      },
      requiredErrorsLastName ()
      {
        return [requiredErrors.bind(this,'lastName')];
      }
    }
    
    function requiredErrors(fieldName, fieldValue)
    {
      switch(fieldName)
      {
        case 'firstName':
          return this.$v[fieldName].$dirty 
                && !this.$v[fieldName].required 
                ? 'First Name is required' : true;
        case 'lastName':
          return this.$v[fieldName].$dirty 
                && !this.$v[fieldName].required 
                ? 'Last Name is required' : true;   
      }
    }
    

    I personally think that this is ugly and would prefer having a separate computed property or method for each field.