Search code examples
javascriptvue.jsvalidationvue-validator

Build custom error message from vue validators


My template contains 2 fields called text, email. The template look as shown below

HTML

    <!DOCTYPE html>
<html>
<head>
    <title>Vue - Validations</title>
    <script src="Libraries/vue/vue.min.js"></script>
    <script src="Libraries/vue/vuelidate.min.js"></script>
    <script src="Libraries/vue/validators.min.js"></script>
</head>
<body>
<div id="app">
    <h2>Form Validation</h2>
    <input v-model="text" v-on:blur="$v.text.$touch()" :class="status($v.text)">
    <!--<p>{{!$v.text.required ? 'Text is required': !$v.text.minLength ? 'Text must be 5 characters' : '' }}</p>-->
    <p style="color:red;">{{textErrMsg}}</p>
    <input v-model="email" v-on:blur="$v.email.$touch()" :class="status($v.email)">
    <p style="color:red;">{{emailErrMsg}}</p>
  <pre>{{ $v }}</pre>
</div>
<script src="app.js"></script>
</body>
</html>

JS

Vue.use(window.vuelidate.default)
const { required, minLength,email, numeric, minValue } = window.validators
new Vue({
    el: "#app",
  data: {
    text: '',
    email: '',
    textErrMsg: '',
    emailErrMsg: ''
  },
  validations: {
    text: {
      required,
      minLength: minLength(5)
    },
    email: {
     required,
     email
    }
  },
  methods: {
    status(validation) {
        return {
        error: validation.$error,
        dirty: validation.$dirty
        
        
        // Want to perform somethins like this
         if(validation.text.required){
           this.textErrMsg == "Text is required";
         } else if(validation.text.minLength){
         this.textErrMsg == "Text must be 5 characters";
         }
         
         if(validation.email.required){
           this.emailErrMsg == "Email is required";
         } else if(validation.email.email){
         this.emailErrMsg == "Enter valid email";
         }
         
      }
    }
  }
})

As commented in the js file, I want to detect the validation fail conditions(i,e required, email, minLength...) so that i can build and show appropriate message in the template.

Instead of doing this stuff in the template.

<p>{{!$v.text.required ? 'Text is required': !$v.text.minLength ? 'Text must be 5 characters' : '' }}</p>

Fiddle link


Solution

  • I've two solutions for you. The first one is based on your Code and the key/value pairs for our inputs. The second one is based on an inputs object. In this solution I iterate over all values and thereby assemble the form.

    I have added an errorMessages object instead of an error string for each value. I have updated it using vm.$set.

    Vue.use(window.vuelidate.default)
    const { required, minLength, email } = window.validators
    
    // SOLUTION 1
    new Vue({
      el: "#app",
      data() {
        return {
          text: '',
          email: '',
          errorMessages: {},
        }
      },
      validations: {
        text: {
          required,
          minLength: minLength(5)
        },
        email: {
          required,
          email
        }
      },
      methods: {
        status(e) {
          const name = e.target.name;
          const currentValidation = this.$v[name];
    
          // first of all reset
          this.$set(this.errorMessages, name, "");
    
          if(!currentValidation.required){
            this.$set(this.errorMessages, name, "Is required");
          } 
          if(typeof currentValidation.minLength !== 'undefined' && !currentValidation.minLength){
            this.$set(this.errorMessages, name, "Must be 5 characters");
          }
    
          // E-Mail check
          if(
            typeof currentValidation.email !== 'undefined' && 
            !currentValidation.email && 
            currentValidation.$invalid
          ) {
            this.$set(this.errorMessages, name, "Must be an email");
          }
    
        }
      }
    });
    
    // -------------------------
    
    // SOLUTION 2
    new Vue({
      el: "#app2",
      data() {
        return {
          inputs: {
            text: {
              value: '',
              title: 'Text',
              error: ''
            }, 
            email: {
              value: '',
              title: 'E-Mail',
              error: ''
            }, 
          },
          errorMessages: {},
        }
      },
      validations: {
        inputs: {
          text: {
            value: {
              required,
              minLength: minLength(5)
            }
          },
          email: {
            value: {
              required,
              email
            }
          },
        }
      },
      methods: {
        edit(e) {
          const value = e.target.value;
          const name = e.target.name;
          const currentValidation = this.$v.inputs[name].value;
          const currentInput = this.inputs[name];
    
          // set the value
          this.$set(currentInput, 'value', value);
    
          // reset
          this.$set(currentInput, 'error', '');
    
          // Want to perform something like this
          if(!currentValidation.required){
            this.$set(currentInput, 'error', "Is required");
          } 
          if(typeof currentValidation.minLength !== 'undefined' && !currentValidation.minLength){
            this.$set(currentInput, 'error',"Must be 5 characters");
          }
    
    
          if(
            typeof currentValidation.email !== 'undefined' && 
            !currentValidation.email && 
            currentValidation.$invalid
          ) {
            this.$set(currentInput, 'error', "Must be an email");
          }
    
        }
      }
    })
    input {
      border: 1px solid silver;
      border-radius: 4px;
      background: white;
      padding: 5px 10px;
    }
    
    .dirty {
      border-color: #5A5;
      background: #EFE;
    }
    
    .dirty:focus {
      outline-color: #8E8;
    }
    
    .error {
      border-color: red;
      background: #FDD;
    }
    
    .error:focus {
      outline-color: #F99;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
    <script src="https://unpkg.com/vuelidate@0.7.5/dist/validators.min.js"></script>
    <script src="https://unpkg.com/vuelidate@0.7.5/dist/vuelidate.min.js"></script>
    
    <div id="app">
      <h3>Form Validation</h3>
      <input 
        v-model="text" 
        name="text"
        @input="status"
        @focus="status"
        @blur="$v.text.$touch()" 
      />
    
      <p style="color:red;">{{errorMessages.text}}</p>
    
      <input 
        v-model="email" 
        name="email"
        @input="status"
        @focus="status"
        @blur="$v.email.$touch()" 
      />
    
      <p style="color:red;">{{errorMessages.email}}</p>
    </div>
    
    <h1>Group</h1>
    
    <div id="app2">
      <h3>Form Validation</h3>
      
      <template v-for="(input, name) in inputs">
        {{input.title}}
        <input 
          :value="input.value"
          :name="name"
          @input="edit"
          @blur="$v[name].$touch()" 
        />
    
        <p style="color:red;">{{input.error}}</p>
      </template>
    
      
      <pre>{{ inputs }}</pre>
    </div>