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) {
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');
}
}
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>
I want my code to select the password textbox when it does not matched with the confirm password textbox after clicking the register button. Click here to see the image of the wanted output
Is there an equivalent way of doing select() function in VueJS? The textbox that I want to be selected is in ref state.
I've tried value.select() but it showed on console that it is not a function.
Just a disclaimer that I'm still learning about VueJS and Quasar. Been googling and reading documents and still no luck.
Here's the code below.
<template>
<div class="window-height window-width row justify-center items-center">
<q-card class="my-card">
<q-card-section class="col-10" style="width: 800px">
<q-input v-model="nameTextBox" label="Name" />
<q-input ref="email" v-model="emailTextBox" type="email" label="Email" />
<q-input ref="password" v-model="passTextBox" type="password" label="Password" />
<q-input v-model="conPassTextBox" type="password" label="Confirm Password" />
<br />
<br />
<q-btn color="secondary" class="q-mr-lg"
#click="registerProfile(emailTextBox, nameTextBox, passTextBox, conPassTextBox)">
Register
</q-btn>
<q-btn :to="'/'">Cancel</q-btn>
</q-card-section>
</q-card>
</div>
</template>
<script setup>
import { ref, getCurrentInstance } from 'vue'
const { $feathers } = getCurrentInstance().appContext.config.globalProperties
const nameTextBox = ref('')
const emailTextBox = ref('')
const passTextBox = ref('')
const conPassTextBox = ref('')
const checkConfirmPassword = (pass, conPass) => {
// console.log(pass, conPass)
if (pass === conPass) {
return true
} else {
return false
}
}
const registerProfile = (emailText, nameText, passText, conPassText) => {
console.log(emailText)
$feathers.service('/users').find({
query: {
email: emailText
}
})
.then(() => {
// email found
alert('Email has already been registered. Please use anothere email.')
// ref.email.select()
})
.catch(() => {
console.log(checkConfirmPassword(passText, conPassText))
if (checkConfirmPassword(passText, conPassText) === false) {
alert('Password is the same. Please retype it again.')
passTextBox.value.select() // -> This syntax does not work.
} else {
console.log('Passed')
$feathers.service('/users').create({
email: emailText,
name: nameText,
password: passText
})
}
})
}
</script>
You are calling the select method on the value of your password textbox (on a string!). You need to define your ref instances and use them. In order to define refs first, define ref names:
const password = ref(null)
const email = ref(null)
then in the HTML part use the exact names for the ref prop:
<q-input ref="email" v-model="emailTextBox" type="email" label="Email" />
<q-input ref="password" v-model="passTextBox" type="password" label="Password" />
and finally, use these variables to reference elements.
password.value.select()
// or
email.value.select()
Full code would be:
<template>
<div class="window-height window-width row justify-center items-center">
<q-card class="my-card">
<q-card-section class="col-10" style="width: 800px">
<q-input v-model="nameTextBox" label="Name" />
<q-input ref="email" v-model="emailTextBox" type="email" label="Email" />
<q-input ref="password" v-model="passTextBox" type="password" label="Password" />
<q-input v-model="conPassTextBox" type="password" label="Confirm Password" />
<br />
<br />
<q-btn color="secondary" class="q-mr-lg"
#click="registerProfile(emailTextBox, nameTextBox, passTextBox, conPassTextBox)">
Register
</q-btn>
<q-btn :to="'/'">Cancel</q-btn>
</q-card-section>
</q-card>
</div>
</template>
<script setup>
import { ref, getCurrentInstance } from 'vue'
const { $feathers } = getCurrentInstance().appContext.config.globalProperties
const nameTextBox = ref('')
const emailTextBox = ref('')
const passTextBox = ref('')
const conPassTextBox = ref('')
// Define refs to components.
// Use this variable name as the one you set in the ref prop for the component.
const password = ref(null)
const email = ref(null)
const checkConfirmPassword = (pass, conPass) => {
// console.log(pass, conPass)
if (pass === conPass) {
return true
} else {
return false
}
}
const registerProfile = (emailText, nameText, passText, conPassText) => {
console.log(emailText)
$feathers.service('/users').find({
query: {
email: emailText
}
})
.then(() => {
// email found
alert('Email has already been registered. Please use anothere email.')
email.value.select() // this works either
})
.catch(() => {
console.log(checkConfirmPassword(passText, conPassText))
if (checkConfirmPassword(passText, conPassText) === false) {
alert('Password is the same. Please retype it again.')
password.value.select() // -> This works know.
} else {
console.log('Passed')
$feathers.service('/users').create({
email: emailText,
name: nameText,
password: passText
})
}
})
}
Please look at this link to figure out how refs work in Vue3 and composition API.
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
I am trying to use vuejs in combination with typescript. Currently I am trying to built a basic form with some validation but visual studio code keeps giving me errors.
I got the first errors from my validate function:
validate(): void {
if((this.$refs.form as Vue & { validate: () => boolean }).validate()) {
this.snackbar = true
}
},
Property 'snackbar' does not exist on type 'CombinedVueInstance
And in my clear function the second error:
clear(): void {
this.$refs.form.clear();
},
Property 'clear' does not exist on type 'Vue | Element | Vue[] |
Element[]'. Property 'clear' does not exist on type
'Vue'.Vetur(2339)
So far the component / FormComponent looks like this:
<template>
<v-container class="fill-height">
<v-row
align="center"
justify="center"
>
<v-form
ref="form"
v-model="valid"
lazy-validation
>
<v-text-field
v-model="name"
:counter="10"
:rules="nameRules"
label="Name"
required
></v-text-field>
<v-text-field
v-model="email"
:rules="emailRules"
label="E-mail"
required
></v-text-field>
<v-select
v-model="select"
:items="items"
:rules="[v => !!v || 'Item is required']"
label="Item"
required
></v-select>
<v-checkbox
v-model="checkbox"
:rules="[v => !!v || 'You must agree to continue!']"
label="Do you agree?"
required
></v-checkbox>
<v-btn
:disabled="!valid"
color="success"
class="mr-4"
#click="storeUser"
>
Validate
</v-btn>
<v-btn
color="error"
class="mr-4"
#click="clear"
>
Leeren
</v-btn>
</v-form>
</v-row>
</v-container>
</template>
<script lang="ts">
import axios from 'axios';
import Vue from 'vue';
import Vuelidate from 'vuelidate'
Vue.use(Vuelidate)
export default Vue.extend({
data: () => ({
valid: true,
name: '',
nameRules: [] = [
(v: any) => !!v || 'Name is required',
(v: any) => (v && v.length <= 10) || 'Name must be less than 10 characters',
],
email: '',
emailRules: [
(v: any) => !!v || 'E-mail is required',
(v: any) => /.+#.+\..+/.test(v) || 'E-mail must be valid',
],
select: null,
items: [
'Item 1',
'Item 2',
'Item 3',
'Item 4',
],
checkbox: false,
}),
methods: {
validate(): void {
if((this.$refs.form as Vue & { validate: () => boolean }).validate()) {
this.snackbar = true // Property 'snackbar' does not exist on type 'CombinedVueInstance<Vue ...
}
},
clear(): void {
this.$refs.form.clear();
//Property 'clear' does not exist on type 'Vue | Element | Vue[] | Element[]'.
//Property 'clear' does not exist on type 'Vue'.Vetur(2339)
},
storeUser(): void {
this.validate();
}
}
})
</script>
Try this.
export default {
data: () => ({
// Property 'snackbar' does not exist
snackbar: false
}),
methods:{
clear(): void {
// Property 'clear' does not exist on type
// formElement.reset()
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/reset
this.$refs.form.reset();
}
}
}