niutech
niutech

Reputation: 29922

How to validate Vuetify text field asynchronously?

Text fields in Vuetify have rules props, which take an array of functions returning true or an error string. How to make them async, so that the validation could be made server-side using XHR?

Something like:

<v-text-field :rules="[v => { axios.get('/check?value=' + val).then(() => { return true }) }]">

Upvotes: 14

Views: 18443

Answers (2)

niutech
niutech

Reputation: 29922

One solution is to set the error-messages prop:

<v-text-field v-model="input" :error-messages="errors">

and use the watch option:

new Vue({
  data () {
    return {
      input: '',
      errors: []
    }
  },
  watch: {
    input (val) {
        axios.get('/check?value=' + val).then(valid => {
          this.errors = valid ? [] : ['async error']
        })
    }
  }
});

Upvotes: 25

Luca
Luca

Reputation: 135

I have to do a backend validation to check if the username entered already exists. I use the swagger client library with the JSON open API v3 to call the method that checks the username value.

So I solved in this way...

In my login.js file I have defined a string property that contains the error message:

import swaggerClient from "../remote-client";
const strict = true;
const state = {
  hasError: false,
  error: null,
  usernameAlredyExists: ""
};
const getters = {
  hasError: state => state.hasError,
  error: state => state.error,
  usernameAlredyExists: state => state.usernameAlredyExists
};
const mutations = {
  checkingUsername(state) {
    state.hasError = false;
    state.error = null;
    state.usernameAlredyExists = "";
  },
  usernameCheckedKO(state) {
    state.usernameAlredyExists = "Username already exists";
  },
  usernameCheckedOK(state) {
    state.usernameAlredyExists = "";
  },
  errorCheckingUsername(state, error) {
    state.hasError = true;
    state.error = error;
  },
};
const actions = {
  userLogin({ commit }, { username, password }) {
    // not relevant code
  },
  checkUsername({ commit }, { username }) {
    commit("checkingUsername");
    swaggerClient.userSwaggerClient
      .then(
        function(client) {
          return client.apis.UserHealthFacility.getHfUserUsernameWithUsername(
            { username: username },
            {},
            {}
          );
        },
        function(reason) {
          // failed to load the JSON specification
          commit("errorCheckingUsername", reason);
        }
      )
      .then(
        function(callResult) {
          if (callResult.body) {
            commit("usernameCheckedKO");
          } else {
            commit("usernameCheckedOK");
          }
        },
        function(reason) {
          // failed to call the API method
          commit("errorCheckingUsername", reason);
        }
      );
  }
};

export default {
  namespaced: true,
  strict,
  state,
  getters,
  mutations,
  actions
};

Then in the Login.vue file I have this code:

<v-card-text>
  <v-form ref="loginForm" v-model="loginValid" lazy-validation>
    <v-text-field
      v-model="username"
      label="Username"
      :rules="[rules.required]"
      :error-messages="usernameAlredyExists"
      v-on:change="callCheckUsername"
    ></v-text-field>
    <v-text-field
      v-model="password"
      :label="Password"
      :append-icon="showPassword ? 'visibility_off' : 'visibility'"
      :type="showPassword ? 'text' : 'password'"
      :rules="[rules.required, rules.minLength]"
      counter
      @click:append="showPassword = !showPassword"
    ></v-text-field>
  </v-form>
</v-card-text>
<v-card-actions>
  <v-spacer></v-spacer>
  <v-btn
    :disabled="!loginValid"
    @click="callUserLogin"
    color="primary"
    round
  >Login</v-btn>
</v-card-actions>

<script>
export default {
  data() {
    return {
      username: "",
      password: "",
      showPassword: false,
      loginValid: true,
      rules: {
        required: value => !!value || "Questo campo è obbligatorio",
        minLength: value =>
          value.length >= 8 || "Questo campo deve contenere almeno 8 caratteri"
      }
    };
  },
  computed: {
    usernameAlredyExists() {
      return this.$store.getters["login/usernameAlredyExists"];
    }
  },
  methods: {
    callUserLogin() {
      if (this.$refs.loginForm.validate()) {
        this.$store.dispatch("login/userLogin", {
          username: this.username,
          password: this.password
        });
      }
    },
    callCheckUsername(value) {
      if (value) {
        this.$store.dispatch("login/checkUsername", {
          username: this.username
        });
      }
    }
  }
};
</script>

In this way it seems to work well

Upvotes: -1

Related Questions