I am using Vue.js 2 and VeeValidate3 to validate my form. This from is also making an axios call to check if the username is already in use. If so, obviously the validation needs to be false.
So far so good. I also see the error message Dieser Name wird bereits verwendet when I type in a username which is already in use.
However, if I see the error message and nevertheless click the submit button, the error message disappears and I can see the message Submit submitCompleteNormalRegistrationForm which gets printed when the form gets submitted.
The question is, why does the form get submitted also there is an validation error with the name? What I am doing wrong?
Also, how can I set the validation for the name to true when the name is not in use?
This is my code so far:
<template>
<div>
<ValidationObserver ref="completeNormalRegistrationForm" v-slot="{ passes }" class="flex-column flex-grow-1 d-flex w-100">
<form #submit.prevent="passes(submitCompleteNormalRegistrationForm)" id="completeNormalRegistrationForm" class="flex-column flex-grow-1 d-flex w-100">
<div class="backButtonWrapper text-left">
<i id="backButtonRegistrationForm" #click="showLoginForm" class="far fa-arrow-alt-circle-left"></i>
</div>
<div class="form-wrapper margin-auto w-100">
<p class="rubik-bold" style="font-size: 1rem;">Registrieren</p>
<ValidationProvider vid="name" name="Nutzername" rules="required|alpha_dash" v-slot="{ errors }">
<input #keyup="completeNormalRegistrationFormUsernameExists" class="form-control search-username" v-model="registerForm.name" type="text" placeholder="Username">
<span v-if="errors[0]" class="username-invalid-span">{{ errors[0] }}</span>
</ValidationProvider>
<ValidationProvider vid="email" name="E-Mail" rules="required|email" v-slot="{ errors }">
<input class="form-control search-email" v-model="registerForm.email" type="email" placeholder="E-Mail">
<span v-if="errors[0]" class="email-invalid-span">{{ errors[0] }}</span>
</ValidationProvider>
<ValidationProvider vid="confirmation" name="Passwort" v-slot="{ errors }">
<input class="form-control" v-model="registerForm.password" type="password" placeholder="Passwort">
<span v-if="errors[0]" class="password-invalid-span">{{ errors[0] }}</span>
</ValidationProvider>
<ValidationProvider rules="confirmed:confirmation" name="Passwort" v-slot="{ errors }">
<input class="form-control" v-model="registerForm.passwordConfirmation" type="password" placeholder="Passwort wiederholen">
<span v-if="errors[0]" class="password-invalid-span">{{ errors[0] }}</span>
</ValidationProvider>
<button type="submit" class="btn btn-primary btn-big big-letter-spacing text-uppercase rubik-bold login">Anmelden</button>
</div>
</form>
</ValidationObserver>
</div>
</template>
<script>
export default {
name: "NavbarAction",
data() {
return {
registerForm: {
name: '',
email: '',
password: '',
passwordConfirmation: '',
termsAndConditions: false,
},
}
},
methods: {
async completeNormalRegistrationFormUsernameExists() {
const nameValid = await this.usernameExists(this.registerForm.name);
if (nameValid) {
this.$refs.completeNormalRegistrationForm.setErrors({name: 'Dieser Name wird bereits verwendet'});
} else {
console.log('Set name is NOT in use!');
}
},
async usernameExists(name){
return await axios.post(window.routes.usernameExists, {value: name})
.then(r => {
return r.data;
});
},
submitCompleteNormalRegistrationForm(){
console.log('Submit submitCompleteNormalRegistrationForm');
console.log(this);
}
}
}
</script>
UPDATE (working with custom rule now):
extend('unique-email', (value) => {
return axios.post(this.routes.emailExists, { value: value })
.then((r) => {
// If email exists, axios response is true
if(r.data){
return {
valid: false,
data: { message: 'E-Mail wird bereits genutzt' }
};
}else{
return {
valid: true,
};
}
}, (err) => {
return {
valid: false,
data: { message: 'E-Mail wird bereits genutzt' }
};
})
},
)
You need to express your email validator as a vee-validate rule instead of trying to do it yourself on keyup. One of the many undocumented things in vee-validate is that if you return a promise as the result of a validation, vee-validate will handle it correctly, waiting to get the result before allowing validation to pass.
Here's an example to get you started:
mounted() {
extend('unique-email', (value) => {
return this.usernameExists(value)
.then((res) => {
return {
valid: true,
};
}, (err) => {
this.$refs.completeNormalRegistrationForm.setErrors({
name: ['Username already registered']
});
})
}, {
immediate: false
})
}
this is front-end validation. The only thing you can do is disable the button when the form is invalid. There is nothing preventing a smart kid trying to submit a form anyway. The true validation should be serverside.
something like:
<button type="submit" class="btn btn-primary btn-big big-letter-spacing text-uppercase rubik-bold login" :disabled="passes(submitCompleteNormalRegistrationForm)">Anmelden</button>
Finally also found a way to set custom error messages without using $refs:
extend('unique-email', (value) => {
return axios.post(window.laravel.emailExists, { value: value })
.then((r) => {
// If email exists, axios response is true
if(r.data){
return "E-Mail wird bereits genutzt";
}else{
return true;
}
}, (err) => {
return "E-Mail wird bereits genutzt";
})
},
);
Related
<template>
<div>
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" v-model="firstName" placeholder="Enter your name">
</div>
<div class="form-group">
<label for="lastName">Last name</label>
<input type="text" class="form-control" v-model="lastName" placeholder="Enter your last name">
</div>
<div class="form-group">
<label for="message">Type Your message</label>
<textarea class="form-control" v-model="message" rows="3"></textarea>
</div>
<div class="form-group form-check" v-for="number in numbers" :key="number">
<input type="checkbox" :value="number.Broj" v-model="checkedNumbers">
<label class="form-check-label" >{{number.Broj}}</label>
</div>
<button type="submit" class="btn btn-primary" v-on:click="alert" #click="sendMessage">Send message</button>
</div>
</template>
<script>
import http from "../http-common.js";
import userServices from "../services/userServices.js";
export default {
data()
{
return {
firstName: null,
lastName: null,
message: null,
numbers: "",
checkedNumbers: [],
success: 'You have submitted form successfully'
};
},
methods:
{
async sendMessage()
{
await http.post("/message", {firstName: this.firstName, lastName: this.lastName, message: this.message, numbers: this.checkedNumbers});
this.$data.firstName = "",
this.$data.lastName = "",
this.$data.checkedNumbers = [],
this.$data.message = "";
},
alert() {
alert(this.success)
if(event)
alert(event.target.tagName)
},
retrieveNumbers() {
userServices.getNumbers().then(response => {
this.numbers = response.data;
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
},
created() {
this.retrieveNumbers();
}
}
</script>
So I want to add the option of checking input fields when user clicks "Send Message" button. I tried some options but I faield at that. So please I would appretiate if someone would help me. I'm still learning.
I know I have to use v-if and create the method for checking the fields.
So if you would be most kind and help me solve this problem I would be really grateful.
Thank you dev, community <3
Can I please get a concrete answer. Because I'll learn in that way, so please without condescending and "no-answers"
You can do it manually :
<script>
import http from "../http-common.js";
import userServices from "../services/userServices.js";
export default {
data()
{
return {
firstName: null,
lastName: null,
message: null,
numbers: "",
checkedNumbers: [],
success: 'You have submitted form successfully'
};
},
methods:
{
async sendMessage()
{
if(!(this.firstName && this.lastName && this.numbers)) return;
await http.post("/message", {firstName: this.firstName, lastName: this.lastName, message: this.message, numbers: this.checkedNumbers});
this.$data.firstName = "",
this.$data.lastName = "",
this.$data.checkedNumbers = [],
this.$data.message = "";
},
alert() {
alert(this.success)
if(event)
alert(event.target.tagName)
},
retrieveNumbers() {
userServices.getNumbers().then(response => {
this.numbers = response.data;
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
},
created() {
this.retrieveNumbers();
}
}
</script>
Or you can this usefull library
https://vuelidate.js.org/#sub-basic-form
You can simply define a method to check the fields and call that before the HTTP request in the sendMessage method.
You can initialize your data as an empty string "" and have a method like this:
validateForm() {
return this.firstName != "" && this.lastName != "" && this.message != ""
}
Update your sendMessage method to something like this:
async sendMessage() {
const isFormValid = this.validateForm()
if (isFormValid) {
await http.post(....)
...
}
}
I'm learning on how to render HTML contents in Vuejs I'm trying to build a small input component which gets generated from render function. It looks something like this:
export default {
name: "nits-input",
methods: {
},
props: {
label: String,
hint: String,
error: String,
placeholder: String
},
render (createElement) {
//Help action text
let helpText = this.hint ? createElement('span', { class: 'm-form__help' }, this.hint) : ''
//Error Text
let errorText = this.error ? createElement('span', { class: 'm--font-danger' }, this.error) : ''
return createElement('div', { class: ''}, [
createElement('label', this.label),
createElement('input', {
class: 'form-control m-input',
attrs: { type: this.type, placeholder: this.placeholder },
domProps: { value: self.value},
on: {
input: function (event) {
this.$emit('input', event.target.value)
}
}
}),
helpText, errorText
])
}
}
While calling this component I'm doing below:
<div class="form-group m-form__group">
<nits-input
label="Email Address"
type="email"
hint="We'll never share your email with anyone else."
placeholder="Enter email"
v-model="email"
>
</nits-input>
</div>
<div class="form-group m-form__group">
<nits-input
label="Password"
type="password"
placeholder="Enter password"
v-model="password"
>
</nits-input>
</div>
I want the value to be stored into v-model, to check the values are being set properly I'm using a watch function
watch: {
email () {
console.log('Email v-model defined as '+this.email)
},
password() {
console.log('Password v-model defined as '+this.password)
}
}
But this always gives me error:
Uncaught TypeError: Cannot read property '$emit' of null
I've taken the references from This VueJS Documentation Link. Help me out in this. Thanks.
you should use arrow function since you're loosing the scope inside that callback :
on: {
input:(event)=> {
this.$emit('input', event.target.value)
}
}
Hope you are doing great!!
I am working on SailsJS web-app and using actions structure for controllers.
This is my user/login action:
module.exports = {
friendlyName: 'Login',
description: 'Login user.',
inputs: {
email: {
description: 'user email.',
type: 'string',
required: true
},
password: {
description: 'user password.',
type: 'string',
required: true
}
},
exits: {
success: {
statusCode: 200,
responseType: 'view',
},
},
fn: async function (inputs, exits) {
// All done.
return 'hello world';
}
};
This is the login form html code:
<div class="row">
<div class="valign-wrapper">
<div class="col s6 z-depth-4 card-panel">
<form class="col s12 login-form" method="POST" action="/login">
<input type="hidden" name="_csrf" value="<%= _csrf %>" />
<div class="card-content">
<h5 class="card-title center-align">Login</h5>
<div class="row margin">
<div class="input-field col s12">
<input id="email" type="email" class="validate">
<label for="email">Email</label>
<div class="invalid-feedback" v-if="formErrors.emailAddress">Please provide a valid email address.</div>
</div>
</div>
<div class="row margin">
<div class="input-field col s12 right">
<input id="password" type="password" class="validate">
<label for="email">Password</label>
</div>
</div>
<div class="row">
<button class="btn waves-effect waves-light right" type="submit" name="action">Login</button>
</div>
</div>
</form>
</div>
</center>
</div>
</div>
Whenever, I am submitting blank form I get this following error:
{
"code": "E_MISSING_OR_INVALID_PARAMS",
"problems": [
"\"email\" is required, but it was not defined.",
"\"password\" is required, but it was not defined."
],
"message": "The server could not fulfill this request (`POST /login`) due to 2 missing or invalid parameters. **The following additional tip will not be shown in production**: Tip: Check your client-side code to make sure that the request data it sends matches the expectations of the corresponding parameters in your server-side route/action. Also check that your client-side code sends data for every required parameter. Finally, for programmatically-parseable details about each validation error, `.problems`. "
}
Now, the issue is that I am unable to find a way to handle this error in prettier way.
I hope one of you can guide me on the right path.
Thank You,
U can transform error in api/responses/badRequest
module.exports = async function badRequest(data) {
const status = 400;
if (data.code && data.code === 'E_MISSING_OR_INVALID_PARAMS'){
return this.res.status(status).json({
code: 'My code',
message: 'My message',
whateva: 'Bla bla'
});
}
return this.res.status(status).json(data);
};
I have a custom project and I need the same thing u requested. I made npm package who take an error object and make it prettier for frontend. Something like this:
module.exports = async function badRequest(data) {
const formatError = require('myFormatError');
const {status, response}= formatError(data);
return this.res.status(status).json(response);
};
It doesn't need to be npm, u can make some function. I hope u were looking for this.
Using the same error object to display will be a mistake because in production technical errors will be stripped off by the toJson method.
The best way to handle the error is to write a custom one. such as
module.exports = {
friendlyName: 'Login',
description: 'Login user.',
inputs: {
email: {
description: 'user email.',
type: 'string'
},
password: {
description: 'user password.',
type: 'string'
}
},
exits: {
success: {
statusCode: 200,
responseType: 'view',
},
badrequest:{
statusCode: 400,
responseType: 'view',
}
},
fn: async function (inputs, exits) {
try{
if(!inputs.email || !inputs.password){
return exits.badrequest({code: 'PARAM_MISSING', message: 'Email/Password is missing'});
}
// all good
return exits.success({/* object containing information realted to success*/});
}
catch(e){
return exists.error(e);
}
}
}
};
I'm trying to use Vee Validate to require either of two input fields, name OR location, but they cannot both be empty. I am getting this error- 'Error in directive validate bind hook: "TypeError: Cannot read property 'expression' of undefined"' Here's what I have-
HTML
<div class="col-xs-12 col-sm-8">
<div class="same-height-parent">
<div class="same-height" :class="{'has-error': errors.has('searchLocation') }">
<input type="text" class="form-control" placeholder="Enter Zip or City, ST" v-model="searchLocation" v-validate ="{ rules: { required: this.locationInput} }" data-vv-name="searchLocation" >
</div>
<div class="form-element same-height">or</div>
<div class="same-height" :class="{'has-error': errors.has('searchName') }">
<input type="text" class="form-control" placeholder="Enter Name" v-model="searchName" v-validate ="{ rules: { required: nameInput} }" data-vv-name="searchName">
</div>
</div>
</div>
<div class="col-xs-12 col-sm-4">
<button class="btn btn-success btn-block btn-fz20"
#click="validateBeforeSubmit()">Search</button>
</div>
JS
export default {
name: 'startNewSearch',
data: function() {
return {
sectionTitle: 'Start a New Search',
sectionClass: 'search',
searchLocation: '',
searchName: '',
searchType: 'location'
}
},
methods: {
validateBeforeSubmit: function(e) {
this.$validator.validateAll();
if (!this.errors.any()) {
this.submit()
}
},
submit: function(e) {}
}
},
computed: {
locationInput() {
if(this.searchName === '') {
return true;
}
return false;
},
nameInput() {
if(this.searchLocation === '')
return true; // cellphone is required
return false;
}
}
}
This is an old question, but I came across it while looking for a similar problem.
Anyhow, I believe you have a syntax error.
v-validate ="{ rules: { required: nameInput} }"
...you're missing 'this'...
v-validate ="{ rules: { required: this.nameInput} }"
Regards,
Wayne
In vee-validate v3 my solution was to extend the require_if rule like this:
app.html:
...
<ValidationProvider name="lastName" immediate rules="required_if_not:company" v-slot="{ errors }">
...
app.js:
extend('required_if_not', {
...required_if,
// params: ['target']
validate: (value, args) => {
let target_value = args.target;
return Boolean(target_value || value);
},
message: 'Bitte dieses Feld ausfüllen.'
});
this also works with multiple fields.
I don't understand why I can't access my datas in the callback response of the POST method of Axios.
I'm trying here to print a error message on error server response but it says into the catch error function that "this" is not defined
here is my code :
<template>
<div class="row">
<div class="form-group">
<label for="exampleInputEmail1">Login</label>
<input type="text" v-model="loginForm" class="form-control" id="exampleInputEmail1" placeholder="login">
</div>
<div class="form-group">
<label for="exampleInputPassword1">Password</label>
<input type="password" v-model="passwordForm" class="form-control" id="exampleInputPassword1" placeholder="Password">
</div>
<button #click="submitForm();" class="btn btn-default">Submit</button>
<div class="row" v-if="errorBool" style="color:red;"></div>
</div>
</template>
<script>
import store from '../../store/store.js'
import Vuex from 'vuex'
import axios from 'axios'
export default {
store: store,
name: 'Login',
data () {
return {
msg: 'Welcome to Login page',
passwordForm: 'admin',
loginForm: 'admin',
errorBool: false,
errorMessage : ''
}
},
computed: {
...Vuex.mapGetters([
'authentification'
]),
},
methods: {
...Vuex.mapActions([
'loadToken',
'isAuth',
'isNotAuth'
]),
submitForm : function() {
axios.post('http://127.0.0.1:5000/login', {
name: this.loginForm,
password: this.passwordForm
})
.then((response) => {
this.loadToken({token: response.data.token})
this.isAuth()
this.$router.push('/dashboard')
this.errorBool = false
})
.catch(function (error) {
console.log(this) // undefinided
this.errorBool = true
this.errorMessage = error
this.isNotAuth()
})
}
},
}
</script>
Just like you did for the then callback, you should use an arrow function for the catch callback, otherwise you lose the desired this binding.
The Promises/A+ specs, point 2.2.5 specifies concerning the two then callback arguments:
onFulfilled and onRejected must be called as functions (i.e. with no this value).3.2
3.2 That is, in strict mode this will be undefined inside of them; in sloppy mode, it will be the global object.
This equally applies to catch, which is just an alternative way for using the second argument of then.
So write:
.catch( error => {
console.log(this) // <-- problem solved.
this.errorBool = true
this.errorMessage = error
this.isNotAuth()
})