Vue.js vuelidate: How to debounce async custom validator?
Asked Answered
A

2

5

I have a validator that checks if username is already registered in database. It worked fine, except for the fact, that the request was send to the server with each character entered - which is way to often. So i tried to debounce the setting of the username variable value but all i'm getting is this:

Uncaught TypeError: Cannot read property 'value' of undefined 
at a.NHnr.q.methods.debounceUsername.H.a.debounce.leading

Vue script:

import uniqueUsername from '../validation/uniqueUsername'
import _ from 'lodash'

export default {
    name: "signUpPage",
    data() {
      return {
        credentials: {
          username: ''
        }
      }
    },
    methods: {
      debounceUsername:
        _.debounce(function (e) {
          this.credentials.username = e.target.value;
        }, 200, {leading: false, trailing: true})
    },
    validations: {
      credentials: {
        username: {
          uniqueUsername: uniqueUsername
        }
      }
   }
}

Html:

    <b-field :label="msg('usernameLabel')"
             :class="{ 'form-group--error': $v.credentials.username.$error }">
      <b-input size="is-large" type="text" class="form__input"
               icon="account" name="username" v-on:input="debounceUsername" autofocus="">
      </b-input>
    </b-field> 
//b-field and b-input are from buefy library

Custom validator (uniqueUsername.js):

import axios from 'axios'

export default value => {
  if (value.trim().length === 0) return true;
  let usernameUnique;
  return new Promise(async function (resolve) {
    await axios('/isUsernameUnique', {
      method: "post",
      data: value,
      headers: {'Content-type': 'text/plain'}
    }).then((response) => usernameUnique = response.data);
    if (usernameUnique) resolve('username is unique');
  });
};
Amorphism answered 8/3, 2018 at 17:44 Comment(0)
A
2

I found the answer. I had to change

this.credentials.username = e.target.value;

to:

this.credentials.username = e;

and it works now - frequency of sending requests is now max one every 200ms

Amorphism answered 9/3, 2018 at 16:58 Comment(0)
W
6

One solution is to check when the user focused out of input field (blur) and then to run the async validations.So:

<input @blur="$v.username.$touch" v-model.lazy="username" />

The script:

export default {
  data () {
   return {
    username: ''
   }
  },
  validations: {
    username: {
     required,
     isUnique(username) {
       if (username === '') return true
       return axios.get('/checkUsername')
                 .then(res => {
                   return res.data //res.data has to return true or false after checking if the username exists in DB
                 }) 
     }
    }
  }
}

Note: in order to work this code you have to import axios and required from vuelidate

Also keep in mind.The backend has to return false if the username is unique in order the above code to work correctly

Witherspoon answered 8/3, 2018 at 21:55 Comment(2)
That's poor UX if you want to provide live updates, though — the user now has to tab out of the field to see if the username's taken, then tab back in to change it.Nubbly
Despite that we have required on username, should we check for username === ''?Scruggs
A
2

I found the answer. I had to change

this.credentials.username = e.target.value;

to:

this.credentials.username = e;

and it works now - frequency of sending requests is now max one every 200ms

Amorphism answered 9/3, 2018 at 16:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.