I'm trying to add validation on login and registration form. button disabled till all form validation is completed after that the button should be able to clickable but its not don't know why is I'm doing something wrong?
I have already done same thing in login form and after filling email and password the disabled button enable and I'm able to login using button but don't know why it's not working in registration form.
screenshot
data() {
return {
email: "",
password: "",
confirmPassword: "",
err_message: "",
errorMessages: "",
err_show: false,
showPass: false,
validate: false,
showConPass: false,
rules: {
required: (value) => !!value || "Required is requred.",
min: (v) => v.length >= 6 || "Min 6 characters",
},
conPass: [
(v) => !!v || "Required is requred.",
(v) => v === this.password || "Passwords do not match",
],
emailRules: [
(value) => {
if (value) return true;
return "E-mail is requred.";
},
(value) => {
if (/.+#.+\..+/.test(value)) return true;
return "E-mail must be valid.";
},
],
};
},
<v-card class="mx-auto py-10 px-10 mt-10" max-width="500" outlined>
<ErrorMessage :err_show="err_show" :err_message="err_message" />
<v-form v-model="validate">
<v-card-title>Register</v-card-title>
<v-text-field
v-model="email"
:rules="emailRules"
:error-messages="errorMessages"
label="Email"
placeholder="example#gmail.com"
required
></v-text-field>
<v-text-field
class="text__Field"
v-model="password"
:append-inner-icon="showPass ? 'mdi-eye' : 'mdi-eye-off'"
:rules="[rules.required, rules.min]"
:type="showPass ? 'text' : 'password'"
name="input-10-1"
label="Password"
hint="At least 6 characters"
counter
#click:append-inner="showPass = !showPass"
></v-text-field>
<v-text-field
class="text__Field"
v-model="confirmPassword"
:append-inner-icon="showPass ? 'mdi-eye' : 'mdi-eye-off'"
:rules="conPass"
:type="showConPass ? 'text' : 'password'"
name="input-10-1"
label="Confirm Password"
hint="At least 6 characters"
counter
#click:append-inner="showConPass = !showConPass"
></v-text-field>
</v-form>
<v-card-actions>
<v-btn :disabled="!validate" elevation="1" #click="register"
>Register</v-btn
>
</v-card-actions>
</v-card>
Related
I have an emailValidation method that I want to run if this.$refs.editUserForm.validate('email') is true but if I enter the email as jack#xyz.com or any valid email it always returns this.$refs.editUserForm.validate('email') as false. Help me find out how can I check the validation of a single field in a form.
<template>
<v-form ref="editUserForm" :model="user" data-app>
<v-text-field
v-model="user.first_name"
label="First Name"
:rules="firstNameRules"
class="required"
required
>
</v-text-field>
<v-text-field
v-model="user.email"
label="Email"
:rules="emailRules"
name="email"
class="required"
required
#blur="emailValidation"
>
</v-text-field>
</v-form>
</template>
<script>
export default {
data: function () {
return {
client: {
first_name: '',
email: ''
},
firstNameRules: [
value => !!value || 'Please enter a first name'
],
emailRules: [
v => /^\w+([.-]?\w+)*#\w+([.-]?\w+)*(\.\w{2,3})+$/.test(v) || 'E-mail must
be valid'
]
};
},
methods: {
emailValidation(){
if (this.$refs.editUserForm.validate('email')) {
console.log("Valid")
}
else {
console.log("Not Valid")
}
}
}
};
</script>
The issue is that the form's validate function does not take a string. It will always validate the whole form and return the result, so even if the email field is valid it can still return false if other fields are invalid.
To validate just the one field, you need to give that field a ref and call validate() on just that one field's ref.
<v-text-field
ref="email"
v-model="user.email"
label="Email"
:rules="emailRules"
name="email"
class="required"
required
#blur="emailValidation"
>
</v-text-field>
The validate code also depends on your version of Vuetify:
Vuetify 2.x
emailValidation() {
const valid = this.$refs.email.validate();
if (valid) {
console.log('Valid');
} else {
console.log('Not Valid');
}
}
Vuetify 3.x
validate() returns a promise so must use async/await
async emailValidation() {
const valid = await this.$refs.email.validate();
// valid is as an array of possible error messages. A length of 0 indicates valid input
if (valid.length === 0) {
console.log('Valid');
} else {
console.log('Not Valid');
}
}
assumption
I am creating a user registration form using Vuetify. I want to display an error if an email address already exists at registration, how can I implement this functionality using Vuetify's textfield?
The configuration is loading the email componentized in the Form. It uses v-model between components with setter and getter to be updated reactively.
What we want to achieve
I want to use v-text-field of Vuetify to show an error if the email address already exists.
Code
Email
UserFormTextFieldEmail
<template>
<v-row justify="center">
<v-col cols="12" md="10" sm="10">
<v-text-field
v-model="setEmail"
type="text"
label="email"
prepend-icon="mdi-email"
:rules="rules"
/>
<p class="caption mb-0" />
</v-col>
</v-row>
</template>
<script>
export default {
props: ['email'],
data () {
return {
rules: [
v => !!v || '',
v => /.+#.+\..+/.test(v) || ''
]
}
},
computed: {
setEmail: {
get () { return this.email },
set (newVal) { return this.$emit('update:email', newVal) }
}
}
}
</script>
Form
<v-card class="pa-7 ma-10 mx-auto" max-width="600">
<div class="login-logo">
<img
:src="logoImg"
width="70px"
>
</div>
<v-form
ref="form"
v-model="isValid"
>
<v-container>
<UserFormTextFieldUserName :name.sync="userInfo.name" />
<UserFormTextFieldEmail :email.sync="userInfo.email" :error.sync="errorMessage" /> // email
<UserFormTextFieldPassword :password.sync="userInfo.password" />
<v-row justify="center">
<v-col cols="12" md="10" sm="10">
<v-btn
:disabled="!isValid || loading"
:loading="loading"
block
class="white--text"
color="deep-purple lighten-1"
#click="signup"
>
・・・
</v-btn>
</v-col>
</v-row>
</v-container>
</v-form>
</v-card>
</div>
</template>
<script>
import '#/assets/css/user-form.scss'
import logoImg from '~/assets/images/login_logo.png'
export default {
auth: false,
data () {
return {
isValid: false,
loading: false,
logoImg,
show: false,
userInfo: {
name: '',
email: '',
password: ''
},
errorMessage:''
}
},
methods: {
signup () {
this.$axios.post('/api/v1/auth', this.userInfo)
.then((response) => {
this.$store.commit('alertSwitchSuccess', true)
setTimeout(() => {
this.$store.commit('alertSwitchSuccess', false)
this.$router.replace(`/user/login`)
}, 2000)
})
.catch((e) => {
})
}
}
}
</script>
I wanted to do the same thing before, and here's my solution:
methods: {
checkemail() {
axios
.get("/api/v1/auth")
.then((response) => {
this.emails = response.data.map((a) =>
a.email);
});
},
},
This function return an Array of all the emails
data() {
return {
//email input v-model
email: "",
//declare the emails array
emails: [],
rules: [
// your rules
(v) =>
(v && this.emails.indexOf(this.email)<0)||
"this email is already existing",
],
I hope that helps
please I need help with this error, app.js:108639 [Vue warn]: Cannot set reactive property on undefined, null, or primitive value: .
I'm working with bootstsrap vue modal and having this error. Here is my code snippet below
Firstly here is my template snippet
<div class="card-tools">
<b-button class="btn btn-success" v-b-modal.modal-prevent-closing>
Add Staff
<i class="fas fa-user-plus fa-fw"></i>
</b-button>
<b-modal
id="modal-prevent-closing"
ref="modal"
title="Enter Staff Info"
#show="resetModal"
#hidden="resetModal"
#ok="handleOk"
>
<form ref="form" #submit.stop.prevent="handleSubmit">
<b-form-group
label="Name"
label-for="name-input"
invalid-feedback="Name is required"
:state="nameState"
>
<b-form-input
id="name-input"
v-model="info.name"
:state="nameState"
required
></b-form-input>
</b-form-group>
<b-form-group
label="Email"
label-for="email-input"
invalid-feedback="Email is required"
:state="emailState"
>
<b-form-input
id="email-input"
v-model="info.email"
:state="emailState"
required
></b-form-input>
</b-form-group>
<b-form-group
label="Phone"
label-for="phone-input"
invalid-feedback="Phone is required"
:state="phoneState"
>
<b-form-input
id="phone-input"
v-model="info.phone"
:state="phoneState"
required
></b-form-input>
</b-form-group>
<b-form-group
label="Job Title"
label-for="job_title-input"
invalid-feedback="Job title is required"
:state="phoneState"
>
<b-form-input
id="phone-input"
v-model="info.job_title"
:state="job_titleState"
required
></b-form-input>
</b-form-group>
</form>
</div>
Now here is my script
import { BButton, BModal, VBModal } from "bootstrap-vue";
export default {
components: {
BButton,
BModal
},
directives: {
'b-modal': VBModal
},
data() {
return {
staffs: {},
nameState: null,
emailState: null,
phoneState: null,
job_titleState: null,
info: {
name: '',
email: '',
phone: '',
job_title: ''
},
showModal: false
}
},
methods: {
getStaffs() {
axios.get("api/user").then(({data }) => (this.staffs = data));
},
// createStaff () {
// this.form.post('api/user')
// .then(({ data }) => { console.log(data) })
// },
checkFormValidity() {
const valid = this.$refs.form.checkValidity()
this.nameState = valid
return valid
},
resetModal() {
this.info = ''
},
handleOk(bvModalEvt) {
// Prevent modal from closing
bvModalEvt.preventDefault()
// Trigger submit handler
this.handleSubmit()
},
handleSubmit() {
// Exit when the form isn't valid
if (!this.checkFormValidity()) {
// return
console.log(this.info)
}
// Hide the modal manually
this.$nextTick(() => {
this.$bvModal.hide('modal-prevent-closing')
})
}
},
created() {
this.getStaffs();
},
mounted() {
console.log('Component mounted.')
}
};
I just wanted to be sure my code is correct before doing the validation.
Thanks in advance
I tried to implement your code into a snippet, and there is one thing with your code that causes an error. It's because you are setting this.info to an empty string, not the object that already is in the data property.
Also, check your b-vue version, which you are using. It may cause warning issues.
I tried not to change your code and simulate your issue as I could understand. Please let me know If I wrote the code with the wrong purpos
new Vue({
el: "#app",
data: function() {
return {
staffs: {},
nameState: null,
emailState: null,
phoneState: null,
job_titleState: null,
info: {
name: '',
email: '',
phone: '',
job_title: ''
},
showModal: false
}
},
methods: {
getStaffs() {
this.staffs = {}
},
checkFormValidity() {
const valid = this.$refs.form.checkValidity()
this.nameState = valid
return valid
},
resetModal() {
this.info = {
name: '',
email: '',
phone: '',
job_title: ''
}
},
handleOk(bvModalEvt) {
// Prevent modal from closing
bvModalEvt.preventDefault()
// Trigger submit handler
this.handleSubmit()
},
handleSubmit() {
// Exit when the form isn't valid
if (!this.checkFormValidity()) {
// return
console.log(this.info)
}
// Hide the modal manually
this.$nextTick(() => {
this.$bvModal.hide('modal-prevent-closing')
})
}
},
created() {
this.getStaffs();
},
mounted() {
console.log('Component mounted.')
}
});
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.js"></script>
<div id="app">
<div class="card-tools">
<b-button class="btn btn-success" v-b-modal.modal-prevent-closing>Add Staff<i class="fas
fa-user-plus fa-fw"></i></b-button>
<b-modal id="modal-prevent-closing" ref="modal" title="Enter Staff Info" #show="resetModal" #hidden="resetModal" #ok="handleOk">
<form ref="form" #submit.stop.prevent="handleSubmit">
<b-form-group label="Name" label-for="name-input" invalid-feedback="Name is required" :state="nameState">
<b-form-input id="name-input" v-model="info.name" :state="nameState" required></b-form-input>
</b-form-group>
<b-form-group label="Email" label-for="email-input" invalid-feedback="Email is required" :state="emailState">
<b-form-input id="email-input" v-model="info.email" :state="emailState" required></b-form-input>
</b-form-group>
<b-form-group label="Phone" label-for="phone-input" invalid-feedback="Phone is required" :state="phoneState">
<b-form-input id="phone-input" v-model="info.phone" :state="phoneState" required></b-form-input>
</b-form-group>
<b-form-group label="Job Title" label-for="job_title-input" invalid-feedback="Job title is required" :state="phoneState">
<b-form-input id="phone-input" v-model="info.job_title" :state="job_titleState" required></b-form-input>
</b-form-group>
</form>
</b-modal>
</div>
</div>
How to disable "Next" button when fields aren't correctly filled out? I would like block this step.
I using passwordRules[] and emailRules[].
:disabled="" working for buttons in specific steps, but I don't know how to use it for inputs rules.
Could someone please help me solve this problem?
Demo code: https://codepen.io/noobmaster2137/pen/JjbZLKz
HTML:
<div id="app">
<v-app id="inspire">
<v-card
class="mx-auto"
width="100%"
max-width="400"
>
<v-card-title class="title font-weight-regular justify-space-between">
<span>{{ currentTitle }}</span>
<v-avatar
color="primary lighten-2"
class="subheading white--text"
size="24"
v-text="step"
></v-avatar>
</v-card-title>
<v-window v-model="step">
<v-window-item :value="1">
<v-card-text>
<v-text-field
v-model="email"
:rules="emailRules"
:counter="50"
label="Email"
minlength="5"
maxlength="50"
required
></v-text-field>
</v-card-text>
</v-window-item>
<v-window-item :value="2">
<v-card-text>
<v-text-field
v-model="password"
:rules="passwordRules"
label="Password"
type="password"
></v-text-field>
<v-text-field
v-model="confirmPassword"
:rules="passwordRules"
label="Confirm Password"
type="password"
></v-text-field>
</v-card-text>
</v-window-item>
<v-window-item :value="3">
<div class="pa-4 text-center">
<v-img
class="mb-4"
contain
height="128"
src="https://cdn.vuetifyjs.com/images/logos/v.svg"
></v-img>
<h3 class="title font-weight-light mb-2">
Welcome to Vuetify
</h3>
<span class="caption grey--text">Thanks for signing up!</span>
</div>
</v-window-item>
</v-window>
<v-divider></v-divider>
<v-card-actions>
<v-btn
:disabled="step === 1"
text
#click="step--"
>
Back
</v-btn>
<v-spacer></v-spacer>
<v-btn
:disabled="step === 3"
color="primary"
depressed
#click="step++"
>
Next
</v-btn>
</v-card-actions>
</v-card>
</v-app>
</div>
JS:
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
step: 1,
email: '',
emailRules: [
v => !!v || 'Email is required',
v => v.length >= 5 || 'The e-mail address must contain at least 5 characters',
v => v.length <= 50 || 'The e-mail address cannot be longer than 50 characters',
v => /.+#.+/.test(v) || 'Please enter a valid email address',
],
password: '',
confirmPassword: '',
passwordRules: [
v => !!v || 'Password is required',
v => v.length >= 8 || 'Password must contain at least 8 characters',
v => v.length <= 50 || 'Password address cannot be longer than 50 characters',
],
}),
computed: {
currentTitle () {
switch (this.step) {
case 1: return 'Sign-up'
case 2: return 'Create a password'
default: return 'Account created'
}
},
},
})
Please check if this helps
Codepen Demo
HTML:
<div id="app">
<v-app id="inspire">
<v-card class="mx-auto" width="100%" max-width="400">
<v-card-title class="title font-weight-regular justify-space-between">
<span>{{ currentTitle }}</span>
<v-avatar color="primary lighten-2" class="subheading white--text" size="24"
v-text="step"
></v-avatar>
</v-card-title>
<v-window v-model="step">
<v-window-item :value="1">
<v-form v-model="isFormValid">
<v-card-text>
<v-text-field v-model="email" :rules="emailRules" :counter="50"
label="Email" minlength="5" maxlength="50" required
></v-text-field>
</v-card-text>
</v-form>
</v-window-item>
<v-window-item :value="2">
<v-form v-model="isFormValid">
<v-card-text>
<v-text-field v-model="password" :rules="passwordRules" label="Password"
type="password"
></v-text-field>
<v-text-field v-model="confirmPassword" :rules="passwordRules"
label="Confirm Password" type="password"
></v-text-field>
</v-card-text>
</v-form>
</v-window-item>
<v-window-item :value="3">
<div class="pa-4 text-center">
<v-img class="mb-4" contain height="128"
src="https://cdn.vuetifyjs.com/images/logos/v.svg"
></v-img>
<h3 class="title font-weight-light mb-2">Welcome to Vuetify</h3>
<span class="caption grey--text">Thanks for signing up!</span>
</div>
</v-window-item>
</v-window>
<v-divider></v-divider>
<v-card-actions>
<v-btn :disabled="!isFormValid ||step === 1" text #click="step--">
Back
</v-btn>
<v-spacer></v-spacer>
<v-btn :disabled=" !isFormValid || step === 3" color="primary" depressed #click="step++">
Next
</v-btn>
</v-card-actions>
</v-card>
</v-app>
</div>
JS:
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
step: 1,isFormValid: false,
email: '',
emailRules: [
v => !!v || 'Email is required',
v => v.length >= 5 || 'The e-mail address must contain at least 5 characters',
v => v.length <= 50 || 'The e-mail address cannot be longer than 50 characters',
v => /.+#.+/.test(v) || 'Please enter a valid email address',
],
password: '',
confirmPassword: '',
passwordRules: [
v => !!v || 'Password is required',
v => v.length >= 8 || 'Password must contain at least 8 characters',
v => v.length <= 50 || 'Password address cannot be longer than 50 characters',
],
}),
computed: {
currentTitle () {
switch (this.step) {
case 1: return 'Sign-up'
case 2: return 'Create a password'
default: return 'Account created'
}
},
},
})
You could use a computed property called "canAdvance" like this
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
step: 1,
email: '',
emailRules: [
v => !!v || 'Email is required',
v => v.length >= 5 || 'The e-mail address must contain at least 5 characters',
v => v.length <= 50 || 'The e-mail address cannot be longer than 50 characters',
v => /.+#.+/.test(v) || 'Please enter a valid email address',
],
password: '',
confirmPassword: '',
passwordRules: [
v => !!v || 'Password is required',
v => v.length >= 8 || 'Password must contain at least 8 characters',
v => v.length <= 50 || 'Password address cannot be longer than 50 characters',
],
}),
computed: {
currentTitle () {
switch (this.step) {
case 1: return 'Sign-up'
case 2: return 'Create a password'
default: return 'Account created'
}
},
canAdvance() {
return this.emailRules.every(rule => rule(this.email) === true) &&
this.passwordRules.every(rule => rule(this.password) === true);
}
},
})
and then use the property to enable or disable the button.
I would return true or false on each rule instead of the error message, something like this
data: {emailRules: [{validator: (v) => {return !!v}, errorMessage: 'Email is required'}]}
so you can separate the validation from the error message and adapt your code to show the corresponding message foreach rule
I am trying to send email through a simple contact form in Vue.js using emailjs. After the mail is sent, I want to update the page so that mail has been sent is displayed instead of the form. I am trying to do this by using v-if on the boolean sent, changing it from false to true on sending the email. But I am not able to access the variable sent from inside the emailjs sendEmail method.
Here is the full code of the contact page:
<template>
<v-container justify-center class="contact-form">
<v-form ref="form" v-model="valid" #submit.prevent="sendEmail" v-if="sent == false">
<v-text-field v-model="name" :rules="namerules" label="Name" name="contact_name" required></v-text-field>
<v-text-field v-model.number="phone" :rules="phonerules" label="Phone number" name="contact_number" required></v-text-field>
<v-text-field v-model="email" :rules="emailrules" label="Email" name="contact_email" required></v-text-field>
<v-textarea v-model="message" :rules="messagerules" label="Message" name="message" required></v-textarea>
<v-btn :disabled="!valid" type="submit" value="Send" color="red" #click="sendEmail">Submit</v-btn>
</v-form>
<v-layout justify-center v-if="sent == true">
<h1>Your Mail Has Been Sent</h1>
</v-layout>
<v-layout justify-center v-if="error == true">
<h2>There was an error sending Your Email. Please Try Again!</h2>
</v-layout>
</v-container>
</template>
<script>
import emailjs from "emailjs-com";
export default {
data() {
return {
valid: false,
sent: false,
error: false,
namerules: [
v => !!v || "Name is Required",
v => /([A-Z])\w+/.test(v) || "Name is Invalid"
],
phonerules: [
v => !!v || "Phone is Required",
v => /^[0-9+]\d*$/.test(v) || "Phone Number is invalid",
v => v != 0 || "Phone Number can't be zeoro!"
],
emailrules: [
v => !!v || "Email is Required",
v => /.+#.+\..+/.test(v) || "Enter a Valid Email"
]
}
},
methods: {
sendEmail: (e) => {
emailjs.sendForm('contact_service', 'contact_form', e.target, 'MY_API_KEY').then((result) => {
this.sent = true;
this.error = false;
console.log('SUCCESS!', result.status, result.text);
}, (error) => {
this.sent = false;
this.error = true;
console.log('FAILED...', error);
});
}
}
};
</script>
<style scoped>
h1 {
color: green;
}
h2 {
color: red;
}
</style>
Edit: Here is How I solved it after getting the answer:
Changed the function to send email as:
methods: {
sendEmail(e) {
this.sending = true;
const self = this;
emailjs.sendForm('contact_service', 'contact_form', e.target, 'user_lYBM45aIuAfPKnfWx0b5n').then(function(result) {
self.sending = false;
self.sent = true;
self.error = false;
console.log('SUCCESS!', result.status, result.text);
}, function (error) {
self.sending = false;
self.sent = false;
self.error = true;
console.log('FAILED...', error);
});
}
}
Your main issue lies with this line:
sendEmail: (e) => {
VueJS documentation explicit states that you should not use arrow functions when declaring methods, because the this inside the methods will not refer to the VueJS component itself.
Note that you should not use an arrow function to define a method (e.g. plus: () => this.a++). The reason is arrow functions bind the parent context, so this will not be the Vue instance as you expect and this.a will be undefined.
Therefore, this will fix your issue:
sendEmail: function(e) {
or:
sendEmail(e) {