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