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 valida
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']
})
}
}
});
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