How do I capture the values of the form in the console. currently am getting an empty object in the console.
<div class="cont">
<v-form
ref="form"
class="form sign-in"
v-model="valid"
lazy-validation
>
<v-text-field
v-model="email"
:rules="emailRules"
label="E-mail"
outlined
required></v-text-field>
<v-text-field v-model="password"
:counter="10"
label="Password"
required
append-icon="mdi-map-marker"
></v-text-field>
<v-btn
:disabled="!valid"
color="success"
class="submit mr-8"
#click="submit" >Submit
</v-btn>
</v-form>
</div>
</template>
<script>
export default {
data: () => ({
form:{
name: '',
email: '',
password: ''
},
valid: true,
email: '',
emailRules: [
v => !!v || 'E-mail is required',
v => /.+#.+\..+/.test(v) || 'E-mail must be valid',
],
}),
methods: {
submit () {
console.log(this.form)
},
},
}
</script>
I want to see the data of the form when you click the submit button
I want to get the form details name email and password.
what i get now is
{__ob__: Observer}email: ""name: ""password: ""__ob__: Observerdep: Dep {_pending: false, id: 246, subs: Array(0)}mock: falseshallow: falsevalue: {__ob__: Observer}vmCount: 0[[Prototype]]: Objectget email: ƒ reactiveGetter()length: 0name: "reactiveGetter"prototype: {constructor: ƒ}arguments: [Exception: TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
You should bind the form property fields to the inputs like v-model="form.name" and v-model="form.email" ..., and remove the other properties name,email and password :
<v-text-field
v-model="form.email"
:rules="emailRules"
label="E-mail"
outlined
required
></v-text-field>
Related
I have an array of objects which I should loop through and show a form for each object's properties. The Save button is out of the for loop. In the attached sandbox, the 2nd object doesn't contain lastname. So, how do I perform validation on click of Save button only for the 2nd form? And is there any way to validate all the forms at once? Please refer to the sandbox for a better understanding.
https://codesandbox.io/s/jolly-kepler-m260fh?file=/src/components/Playground.vue
Check this codesandbox I made: https://codesandbox.io/s/stack-72356987-form-validation-example-4yv87x?file=/src/components/Playground.vue
You can validate all v-text-field at once if you move the v-form outside the for loop. All you need to do is give the form a ref value and a v-model to make use of the built in vuetify validation methods.
<template>
<v-container class="pl-10">
<v-form ref="formNames" v-model="validForm" lazy-validation>
<v-row v-for="(name, index) in names" :key="index">
<v-col cols="12">
<v-text-field
v-model="name.firstName"
outlined
dense
solo
:rules="rulesRequired"
/>
</v-col>
...
</v-row>
</v-form>
<v-btn type="submit" #click="submitForm" :disabled="!validForm">
Submit
</v-btn>
</v-container>
</template>
Then in the submit button all you need to do is call the validate() method of the form through the $refs object. You can also disable the submit button if any of the elements in the form don't pass the validation rules using the v-model of the form to disable the submit button.
<script>
export default {
name: "playground",
data: () => ({
validForm: true,
names: [
{
firstName: "john",
lastName: "doe",
age: 40,
},
{
firstName: "jack",
lastName: "",
age: 30,
},
],
rulesRequired: [(v) => !!v || "Required"],
}),
methods: {
submitForm() {
if (this.$refs.formNames.validate()) {
// Form pass validation
}
},
},
};
</script>
As submit button is outside of the forms. We can perform that validation on submit event by iterating the names array and check if any value is empty and then assign a valid flag value (true/false) against each object.
Demo :
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
names: [
{
firstName: "john",
lastName: "doe",
age: 40,
valid: false
},
{
firstName: "jack",
lastName: "",
age: 30,
valid: false
},
],
requiredRule: [v => !!v || 'Value is required']
}),
methods: {
submitForm() {
this.names.forEach((obj, index) => {
if (!obj.lastName) {
obj.valid = false;
console.log(
`${index + 1}nd form is not valid as LastName is not available`
);
} else {
obj.valid = true;
}
});
// Now you can filter out the valid form objects based on the `valid=true`
}
}
})
<script src="https://unpkg.com/vue#2.x/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify#2.6.6/dist/vuetify.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/vuetify#2.6.6/dist/vuetify.min.css"/>
<div id="app">
<v-app id="inspire">
<v-container class="pl-10">
<v-row v-for="(name, index) in names" :key="index">
<v-form v-model="name.valid">
<v-col cols="12">
<v-text-field v-model="name.firstName" outlined dense solo required :rules="requiredRule" />
</v-col>
<v-col cols="12">
<v-text-field v-model="name.lastName" outlined dense solo required :rules="requiredRule" />
</v-col>
<v-col cols="12">
<v-text-field v-model="name.age" outlined dense solo required :rules="requiredRule" />
</v-col>
</v-form>
</v-row>
<v-btn type="submit" #click="submitForm"> Submit </v-btn>
</v-container>
</v-app>
</div>
I have a Vue 2 app that works perfectly in development. However, in production one of the pages renders as blank. No error messages, no 404. I can't find what is different for this page vs the others that work fine. My router page looks like this:
import Vue from "vue";
import VueRouter from "vue-router";
Vue.use(VueRouter);
const routes = [
{
path: "/",
name: "Login",
component: () =>
import("../views/Login.vue"),
},
{
path: "/main",
name: "Main",
component: () =>
import("../views/Main.vue"),
},
{
path: "/register",
name: "Register",
component: () =>
import("../views/Register.vue"),
},
{
path: "/login",
name: "Login",
component: () =>
import("../views/Login.vue"),
},
{
path: "/players",
name: "Players",
component: () =>
import("../views/Players.vue"),
},
{
path: "/invitations",
name: "Invitations",
component: () =>
import("../views/Invitations.vue"),
},
{
path: "/editplayer",
name: "Editplayer",
component: () =>
import("../views/Editplayer.vue"),
},
{
path: "/message",
name: "Message",
component: () =>
import("../views/Message.vue"),
},
];
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes,
});
export default router;
The .vue file looks like this:
<template>
<v-card color="basil">
<v-card-title class="text-center justify-center py-6">
<h2 class="font-weight-bold blue--text">
Sign Up
</h2>
</v-card-title>
<v-card
color="basil"
flat
>
<!--- Start registration form --->
<v-card-text >
<v-form v-model="valid" >
<v-container>
<v-row>
<v-col
cols="12"
md="4"
>
<v-text-field
v-model="invitationCode"
label="Invitation Code"
tabindex="1"
required
></v-text-field>
</v-col>
<v-col
cols="12"
md="4"
>
<v-text-field
v-model="firstname"
:rules="firstRules"
:counter="15"
label="First name"
required
tabindex="2"
></v-text-field>
</v-col>
<v-col
cols="12"
md="4"
>
<v-text-field
v-model="lastname"
:rules="lastRules"
:counter="15"
label="Last name"
required
tabindex="3"
></v-text-field>
</v-col>
<v-col
cols="12"
md="4"
>
<v-text-field
v-model="displayname"
:rules="displayRules"
:counter="40"
label="Name you want others to see"
required
tabindex="4"
></v-text-field>
</v-col>
<v-col
cols="12"
md="4"
>
<v-text-field
v-model="email"
:rules="emailRules"
label="E-mail"
required
tabindex="5"
></v-text-field>
</v-col>
<v-col
cols="6"
md="6"
>
Phone <br />
<v-text-field
v-model="phone"
hint="Numbers Only!"
required
tabindex="6"
></v-text-field>
</v-col>
<v-col
cols="12"
md="4"
>
<v-text-field
v-model="password"
:append-icon="show1 ? 'mdi-eye' : 'mdi-eye-off'"
:rules="[rules.required, rules.min]"
:type="show1 ? 'text' : 'password'"
name="passowrd"
label="Password"
hint="At least 8 characters"
class="input-group--focused"
#click:append="show1 = !show1"
tabindex="7"
></v-text-field>
</v-col>
<v-col
cols="12"
md="4"
>
<v-text-field
v-model="password2"
:append-icon="show1 ? 'mdi-eye' : 'mdi-eye-off'"
:rules="[rules.required, rules.min]"
:type="show1 ? 'text' : 'password'"
name="password2"
label="Re-enter your password"
hint="At least 8 characters"
class="input-group--focused"
#click:append="show1 = !show1"
tabindex="8"
></v-text-field>
</v-col>
</v-row>
<v-btn tabindex="9"
class="orange" #click="register()"><v-icon>mdi-check</v-icon></v-btn>
</v-container>
</v-form>
</v-card-text>
</v-card>
</v-card>
</template>
<script>
import EventService from '../Services/EventServices'
export default {
name: "Register",
data () {
return {
valid: false,
firstname: '',
lastname: '',
phone: '',
firstRules: [
v => !!v || 'First name is required',
v => v.length <= 10 || 'Name must be less than 15 characters',
],
lastRules: [
v => !!v || 'Last name is required',
v => v.length <= 10 || 'Name must be less than 15 characters',
],
displayRules: [
v => !!v || 'Display name is required',
v => v.length <= 40 || 'Name must be less than 40 characters',
],
phoneRules: [
v => !!v || 'Phone number is required',
v => v.length <= 40 || 'Name must be less than 40 characters',
],
displayname: '',
userInfo: [],
password: '',
password2: '',
show1: false,
message: '',
invitationCode: '',
email: '',
emailRules: [
v => !!v || 'E-mail is required',
v => /.+#.+/.test(v) || 'E-mail must be valid',
],
rules: {
required: value => !!value || 'Required.',
min: v => v.length >= 8 || 'Min 8 characters',
emailMatch: () => (`The email and password you entered don't match`),
},
}
},
methods: {
async register() {
// console.log('inlogin.vue, starting register ')
var areaCode = this.phone.substr(0, 3)
var prefixCode = this.phone.substr(3,3)
var phoneLine = this.phone.substr(6,4)
await EventService.registration(this.email, this.firstname, this.lastname, this.displayname, this.password, areaCode, prefixCode, phoneLine, this.invitationCode)
.then(
(registerReturn => {
if (registerReturn === 'invalid') {
this.regError = "Invalid Invitation Code with this Email Address."
} else {
// console.log('registered', registerReturn)
this.$router.push("/")
}
})
);
}
}
}
</script>
<style>
/* Helper classes */
.basil {
background-color: #FFFBE6 !important;
}
.basil--text {
color: #356859 !important;
}
</style>
If it helps, the live page is at https://0-0-2.net/register. I'm not sure where to start troubleshooting this since it works in development. I'm guessing there's something breaking during the build process? How would you suggest I work through that?
match, thank you for the reminder about going back to the basics. I replaced the file and it still did not work. But it did at least eliminate a problem with that file.
For anyone seeing this later, what I did to fix the problem was
--Rename the file to Registration.vue
--changed
export default {
name: "Register",
to:
export default {
name: "Registration",
--Updated the route accordingly. Reran the "npm run build" command.
All worked fine. My best guess is some kind of caching problem somewhere along the line. It may not have gotten to the root problem but it got my app working and kept my efforts moving along.
I want to make custom validation with Vuetify. My Vuetify version is 1.11.3. Here is my template. I set a ref to v-card according to this documentation.
<v-card ref="form">
<v-card-text>
<v-text-field
ref="name"
v-model="name"
label="Full Name:"
:rules="[() => !!name || 'Name is required']"
:error-messages="errorMessages"
required
light
>
</v-text-field>
<v-text-field
ref="email"
v-model="email"
label="Email Address:"
:rules="[
() => !!email || 'Email is required',
() => (!!email && /.+#.+/.test(email)) || 'Email must be valid',
]"
required
light
></v-text-field>
<VuePhoneNumberInput
ref="phone"
v-model="phone"
color="black"
dark-color="white"
size="lg"
default-country-code="BD"
light
required
/>
<v-textarea
ref="msg"
v-model="msg"
label="Message"
:rules="[() => !!msg || 'Message is required']"
light
></v-textarea>
</v-card-text>
<v-card-actions>
<v-btn #click="sendForm"> Submit </v-btn>
</v-card-actions>
</v-card>
I am trying to validate the form and textfields with their references.
This is my code:
data() {
return {
name: null,
email: null,
phone: null,
msg: null,
submitStatus: null,
formHasErrors: false,
errorMessages: '',
}
},
computed: {
form() {
return {
name: this.name,
email: this.email,
phone: this.phone,
msg: this.msg,
}
},
},
watch: {
name() {
this.errorMessages = ''
},
},
methods: {
sendForm() {
this.formHasErrors = false
Object.keys(this.form).forEach((f) => {
if (!this.form[f]) this.formHasErrors = true
this.$refs[f].validate(true)
})
}
When I submit the button, It shows
client.js?06a0:103 TypeError: _this.$refs[f].validate is not a
function
I get following error. What is the wrong with that?
I would recommence using V-form instead of v-card here.
Then you can check if your form is valid with the function this.$ref.myForm.validate() which returns a boolean
Here is a small example:
<v-card>
<v-card-text>
<v-form ref="myForm">
<v-row>
<v-col
cols="12"
sm="7"
>
<v-text-field
prepend-icon="mdi-tag-text"
v-model="form.name"
:rules="[
(v) => !!v || 'Name is requierd',
]"
label="Name"
/>
</v-col>
</v-row>
</v-form>
</v-card-text>
<v-card-actions>
<v-btn
#click="check"
>
Check
</v-btn>
</v-card-actions>
</v-card>
And the script :
export default {
data: () => ({
form:{
name : ""
},
}),
methods:{
check(){
if (this.$refs.myForm.validate()){
//form is valid
} else {
//form is not valid
}
}
}
}
I'm breaking my head for a few days now, trying to figure out how to grab the data from child components.
Situation is like this.
I have one parent component called Post where user can select date, title, description and which can contain multiple instances of Event compontents.
Event component contains fields like title, description, attendees.
User should be able to add multiple Eventcomponents which means I have multiple components Event within the Post component.
So, I can't figure out how can I compose my components to have an array of Event objects inside my Post component which I can later on send to my API.
the structure of the post object I need is:
// Post.vue
{
"date": '',
"name": '',
"description": '',
"events": {
{
"title": '',
"description": '',
"attendees": ''
},
{
"title": '',
"description": '',
"attendees": ''
}
}
}
So, I don't know should and how I would use vuex for it. I've tried using $emit to pass the data but I couldn't find it fit to get the data into Post model.
Can someone point me where should I look for it?
EDIT #1: Added sample code
The code for the components:
<template>
<v-form>
<v-container>
<v-row>
<v-col
cols="12"
md="4"
>
<v-date-picker v-model="post.date" scrollable>
<v-spacer />
<v-btn text color="primary" #click="modal = false">
Cancel
</v-btn>
<v-btn text color="primary" #click="$refs.dialog.save(date)">
OK
</v-btn>
</v-date-picker>
</v-col>
<v-col
cols="12"
md="4"
>
<v-text-field
v-model="post.name"
label="name"
required
/>
</v-col>
<v-col
cols="12"
md="4"
>
<v-textarea
v-model="post.description"
name="description"
label="Description"
dense
value
rows="4"
hint
/>
</v-col>
</v-row>
<v-row>
<v-btn primary rounded #click="addLine">
Add Event
</v-btn>
<v-expansion-panels accordion>
<UserEvent
v-for="(line, index) in lines"
:key="index"
#addLine="addLine"
#removeLine="removeLine(index)"
/>
</v-expansion-panels>
</v-row>
</v-container>
</v-form>
</template>
<script>
import UserEvent from './partials/event'
export default {
name: 'Post',
components: { UserEvent },
data () {
return {
post: [],
lines: [],
blockRemoval: true
}
},
watch: {
lines () {
this.blockRemoval = this.lines.length <= 1
}
},
mounted () {
},
methods: {
addLine () {
const checkEmptyLines = this.lines.filter(line => line.number === null)
if (checkEmptyLines.length >= 1 && this.lines.length > 0) { return }
this.lines.push({
title: null,
description: null,
attendees: null
})
},
removeLine (lineId) {
if (!this.blockRemoval) { this.lines.splice(lineId, 1) }
}
}
}
</script>
And the child component UserEvent
// UserEvent.vue
<template>
<v-expansion-panel>
<v-expansion-panel-header>Event details</v-expansion-panel-header>
<v-expansion-panel-content>
<v-row>
<v-col cols="12" md="6">
<v-text-field
v-model="event.title"
label="Title"
required
/>
</v-col>
<v-col
cols="12"
md="6"
>
<v-text-field
v-model="event.atttendees"
label="Atendees"
required
/>
</v-col>
<v-col
cols="12"
md="12"
>
<v-textarea
v-model="event.description"
name="description"
label="Description"
dense
value
rows="4"
hint
/>
</v-col>
<v-col
cols="12"
md="3"
>
<div class="block float-right">
<v-btn #click="removeLine(index)" />
<v-btn v-if="index + 1 === lines.length" #click="addLine" />
</div>
</v-col>
</v-row>
</v-expansion-panel-content>
</v-expansion-panel>
</template>
<script>
export default {
name: 'UserEvent',
props: ['line', 'index'],
data () {
return {
event: []
}
},
methods: {
addLine () {
this.$emit('addLine')
},
removeLine (index) {
this.$emit('removeLine', index)
}
}
}
</script>
Here's an example with a similar structure what was posed in the question:
{
name: String,
events: [
title: String,
description: String,
],
}
This example allows the user to open a form to add a new event. When that form is submitted, the event data is added to the parent component's state.
Parent
<template>
<div>
<input v-model="name" />
<ul v-if="events.length">
<li v-for="(event, index) in events" :key="index">
<span>{{ event.title }}</span>
<span>{{ event.description }}</span>
</li>
</ul>
<Event v-if="isNewEventFormVisible" #submit="addEvent" />
<button v-else #click="showNewEventForm">add event</button>
</div>
</template>
import Event from '~/components/Event';
export default {
components: { Event },
data() {
return {
name: 'Example Post',
events: [],
isNewEventFormVisible: false,
};
},
methods: {
addEvent({ title, description }) {
this.isNewEventFormVisible = false;
this.events.push({ title, description });
// TODO: call you API here to update
},
showNewEventForm() {
this.isNewEventFormVisible = true;
},
},
};
Event
<template>
<form #submit.prevent="onSubmit">
<input v-model.trim="title" type="text" />
<br />
<textarea v-model.trim="description" />
<button type="submit">submit</button>
</form>
</template>
export default {
data() {
return {
title: '',
description: '',
};
},
methods: {
onSubmit() {
this.$emit('submit', {
title: this.title,
description: this.description,
});
},
},
};
You could imagine a more sophisticated version of this where events are editable. In that case, each Event could take props and bind them as values to its input instead of using v-models.
temp.vue
<v-form ref="entryForm" #submit.prevent="save">
<v-text-field label="Amount" :rules="numberRule"r></v-text-field>
<v-btn type="submit">Save</v-btn>
</v-form>
<script>
export default {
data: () => ({
numberRule: [
v => !!v || 'Field is required',
v => /^\d+$/.test(v) || 'Must be a number',
],
}),
methods: save () {
if (this.$refs.entryForm.validate()){
//other codes
}
}
}
</script>
What happens here is while typing in the text field itself the rule gets executed. I want to execute the rule only on submit. How to do that in vuetify text field?
Vuetify rules are executed when the input gets value,
But if you want that to happen only on the form submit, you have remodified the rules that are being bound to that input,
Initially, rules should be an empty array, when you click on the button you can dynamically add/remove the rules as you wanted, like this in codepen
CODEPEN
<div id="app">
<v-app id="inspire">
<v-form ref="entryForm" #submit.prevent="submitHandler">
<v-container>
<v-row>
<v-col
cols="12"
md="6"
>
<v-text-field
v-model="user.number"
:rules="numberRules"
label="Number"
required
></v-text-field>
</v-col>
</v-row>
<v-row>
<v-btn type="submit" color="success">Submit</v-btn>
</v-row>
</v-container>
</v-form>
</v-app>
</div>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
valid: false,
firstname: '',
user: {
number: ''
},
numberRules: []
}),
watch: {
'user.number' (val) {
this.numberRules = []
}
},
methods: {
submitHandler () {
this.numberRules = [
v => !!v || 'Field is required',
v => /^\d+$/.test(v) || 'Must be a number',
]
let self = this
setTimeout(function () {
if (self.$refs.entryForm.validate()){
//other codes
alert('submitted')
}
})
}
}
})
If you're like me and just want to prevent validation from running on every key stroke, apply validate-on-blur prop on your text fields and now validation will only be perform after user has completed typing the whole input.
So not an exact answer to the OP, but I think this is what most of us want to achieve. This prop has been documented here.
I have another way to solve this problem without setting up watchers:
<v-form lazy-validation v-model="valid" ref="form">
<v-text-field
class="w-100"
light
label="Nome"
v-model="form.nome"
:rules="[rules.required]"
rounded
required
outlined
hide-details="auto"
></v-text-field>
<v-btn
rounded
height="50"
width="200"
:disabled="!valid"
:loading="isLoading"
class="bg-btn-secondary-gradient text-h6 white--text"
#click="submitContactForm()"
>
Enviar
</v-btn>
</v-form>
There is a prop called lazy-validation on vuetify, as you can see on the docs: https://vuetifyjs.com/en/api/v-form/#functions
So, the v-form has a method that you can see through $refs called validate(), and it can return true or false, based on your form rules.
And, the function that will trigger the validation on submit will be like this:
submitContactForm() {
const isValid = this.$refs.form.validate();
if (isValid) {
alert("Obrigado pelo contato, sua mensagem foi enviada com sucesso!");
this.form = {
nome: "",
celular: "",
email: "",
mensagem: ""
};
this.$refs.form.resetValidation(); // Note that v-form also has another function called resetValidation(), so after we empty our fields, it won't show the validation errors again.
}
},