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();
}
}
}
Related
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
Hi everyone I'm new to Vue 3 and Typescript and I'm having hard time trying to solve typescript error on my project below is the typescript error.
It's not able to find validate method but it's showing when I try to console it.
Property 'validate' does not exist on type 'string'.
I'm using Vue 3 composition api with typescript and Vue Element Plus, below is my component code.
Component
<template>
<div class="w-full h-full absolute flex">
<el-form
class="w-4/5 sm:w-1/2 lg:w-1/3 xl:w-1/4 m-auto"
:ref="formRef"
:model="form"
:rules="rules"
>
<h2>Login</h2>
<el-form-item label="Email" prop="email">
<el-input
type="email"
autocomplete="off"
v-model="form.email"
/>
</el-form-item>
<el-form-item label="Password" prop="password">
<el-input
type="password"
autocomplete="off"
v-model="form.password"
/>
</el-form-item>
<el-form-item>
<el-button
type="success"
class="w-full"
#click="handleLogin"
>Submit</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script lang="ts">
import { defineComponent, onBeforeMount, onMounted, reactive, ref } from "vue";
export default defineComponent({
name: "Login",
setup() {
const form = reactive({
email: "",
password: ""
});
const rules = ref({
email: [{
required: true,
message: "Email is required",
trigger: "blur"
}],
password: [{
required: true,
message: "Password is required",
trigger: "blur"
}]
});
const formRef = ref("formRef");
const handleLogin = () => {
console.log(formRef)
// Property 'validate' does not exist on type 'string'.
formRef.value?.validate((valid: boolean) => {
console.log(valid);
});
};
onMounted(() => {
console.log("Login mounted");
});
onBeforeMount(() => {
console.log("Login onBeforeMount");
});
return {
formRef,
form,
rules,
handleLogin
};
}
});
</script>
There is a few mistakes. ref receives a primitive type string, boolean, etc... for objects use reactive.
so you should define rules using reactive not ref.
const rules = reactive({
email: [{
required: true,
message: "Email is required",
trigger: "blur"
}],
password: [{
required: true,
message: "Password is required",
trigger: "blur"
}]
});
You don't need to bind your ref so do this way:
<el-form
class="w-4/5 sm:w-1/2 lg:w-1/3 xl:w-1/4 m-auto"
ref="formRef"
:model="form"
:rules="rules"
>
And I recommend you to declare you formRef using the html casting objects.
like this:
const formRef = ref<HTMLFormElement>();
I hope it help you =)
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) {
I'm starting a project in which I had to use Vue. I'm actually really new to this, so I'm learning on the go. I do apologize in advance since this question have answered before, however, I didn't really understand the solutions provided, which is why I'm here asking myself.
Well, I was trying to display some data on my Data Table (more specifically, v-data-table from Vuetify). I was able to get the data from the API, but, for some reason it doesn't show me anything. Thanks to Vuex I can see that the mutation worked because on the console on Google Chrome I can see the Array of objects. But as I said, it still does't show me a single thing on the table, it even says 'no data available'. Some errors that I get are things like '[Vue warn]: Invalid prop: type check failed for prop "items". Expected Array, got Object' and 'TypeError: this.items.slice is not a function'.
Here is the code from List.vue
<template>
<v-container id="data-tables" tag="section">
<div class="text-right">
<v-btn class="mx-2" fab dark color="primary" :to="{ name: 'UserCreate' }">
<v-icon dark>mdi-plus</v-icon>
</v-btn>
</div>
<base-material-card
color="indigo"
icon="mdi-vuetify"
inline
class="px-5 py-3"
>
<template v-slot:after-heading>
<div class="display-2 font-weight-light">
Lista de Empleados
</div>
</template>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
class="ml-auto"
label="Search"
hide-details
single-line
style="max-width: 250px;"
/>
<v-divider class="mt-3" />
<v-data-table
:headers="headers"
:items="users"
:search.sync="search"
:sort-by="['name', 'office']"
:sort-desc="[false, true]"
multi-sort
>
<template v-slot:item.actions="{ item }">
<v-icon small class="mr-2" #click="editItem(item)">
mdi-eye
</v-icon>
<v-icon
small
class="mr-2"
#click="editItem(item)"
:to="{ name: 'UserUpdate' }"
>
mdi-pencil
</v-icon>
<v-icon small #click="deleteItem(item)">
mdi-delete
</v-icon>
</template>
</v-data-table>
</base-material-card>
</v-container>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'UsersTable',
data() {
return {
headers: [
{
text: 'Nombre',
value: 'empleado.nombre',
},
{
text: 'Apellido',
value: 'empleado.apellido',
},
{
text: 'Dirección',
value: 'empleado.direccion',
},
{
text: 'Correo Electrónico',
value: 'email',
},
{
text: 'Teléfono',
value: 'empleado.telefono',
},
{
sortable: false,
text: 'Actions',
value: 'actions',
},
],
loader: true,
search: undefined,
}
},
created() {
this.$store.dispatch('users/fetchUsers')
},
computed: {
...mapState(['users']),
},
methods: {},
mounted() {},
}
</script>
And the code from user.js, where the fetchUsers it's coming from.
import auth from '#/api/auth'
export const namespaced = true
export const state = {
users: [],
}
export const mutations = {
SET_USERS(state, users) {
state.users = users
},
}
export const actions = {
fetchUsers({ commit, dispatch }) {
auth
.getAllAccounts()
.then((response) => {
commit('SET_USERS', response.data)
})
.catch((error) => {
const notification = {
type: 'error',
message: 'There was a problem fetching users: ' + error.message,
}
dispatch('notification/add', notification, { root: true })
})
},
}
Thanks in advance.
You are not getting the correct user from vuex, because is namespaced, change to:
computed: {
...mapState('users',['users']),
},
MapState helper dosen't work the same way like the other helpers because the state module isn't registred in the global namespace. So namespacing your module will help or you do it in this way:
computed: {
...mapState({
users: state => state.FilenameOfYourModule.users
})
}
I want to build register form in Vue.
I think I did everything okay by the book. But my question is how to trigger the validation if I using v-form from Vuetify and vue-property-decorator.
Because all the examples they have this.$refs.form.validate()... For this form it's not working.
So, how to trigger the validation when I submit the form?
This is my code:
<template>
<v-container fluid fill-height>
<v-layout align-center justify-center>
<v-flex xs12 sm8 md6>
<v-container>
<v-card>
<v-toolbar dark color="primary">
<v-toolbar-title>Register</v-toolbar-title>
</v-toolbar>
<v-card-text>
<v-form v-model="loginValid">
<v-text-field v-model="form.name.value" prepend-icon="person" name="Name" label="Name" required></v-text-field>
<v-text-field v-model="form.email.value" :rules="form.email.rule" label="Email" required type="email" prepend-icon="person"></v-text-field>
<v-text-field prepend-icon="lock" v-model="form.password.value" :rules="form.password.rule" label="Password" type="password" required></v-text-field>
</v-form>
</v-card-text>
<v-card-actions class="pa-3">
<v-spacer></v-spacer>
<v-btn ref="btn-entrar" id="btn-entrar" color="primary" #click="submit">Register</v-btn>
<router-link to="/login" class="btn btn-link">Login</router-link>
</v-card-actions>
</v-card>
</v-container>
</v-flex>
</v-layout>
</v-container>
</template>
<script lang="ts">
import { Component, Watch, Prop } from 'vue-property-decorator';
import BaseComponent from '#/modules/common/components/baseComponent.vue';
import { State, Action, Getter } from 'vuex-class';
#Component({})
export default class RegisterPage extends BaseComponent {
public loginValid: boolean = false;
public form = {
name: { value: '' },
email: {
value: '',
rule: [
(v: string) => !!v || 'Email is required',
(v: string) => /.+#.+/.test(v) || 'E-mail must be valid'
]
},
password: {
value: '',
rule: [
(v: string) => !!v || 'Password is required',
(v: string) => v.length >= 8 || ''
]
}
};
public name: string = '';
public email: string = '';
public password: string = '';
constructor() {
super();
}
public submit() {
// i don't know if this form is valid or not :(
console.log('in submit');
}
}
</script>
I have not used Typescript, but ultimately, you'll have to validate each model onSubmit. So, in the submit() function that you have, do preventDefault(), validate your fields and if all okay then go ahead actually do submit the form to the backend.
Do read the guide for general workflow: https://v2.vuejs.org/v2/cookbook/form-validation.html
Also, do check out Vuelidate and VeeValidate which are simple validation frameworks for VueJS.
p.s.: see: using Vuelidate with Typescript issue first for good pointers.