Server side form validation with vue.js and vuetify - vue.js

I'm seeing lots of documentation for client-side validation with Vuetify, but finding it very difficult to find docs for server side validation messages for vuetify and vue.
PROBLEM
I have this component:
<template>
<v-container>
<v-layout row>
<v-flex xs12 sm6 offset-sm3>
<v-card>
<v-card-text>
<v-container>
<h3>Register Now</h3>
<form v-on:submit.prevent="onSubmit">
<v-layout row>
<v-flex xs12>
<v-text-field
name="email"
label="Email"
type="email"
ref="user_email"
id="email">
</v-text-field>
</v-flex>
</v-layout>
<v-layout row>
<v-flex xs12>
<v-text-field
name="password"
label="Password"
type="password"
ref="user_password"
id="password">
</v-text-field>
</v-flex>
</v-layout>
<v-layout row>
<v-flex xs12>
<v-btn type="submit">Sign Up</v-btn>
</v-flex>
</v-layout>
</form>
</v-container>
</v-card-text>
</v-card>
</v-flex>
</v-layout>
</v-container>
</template>
<script>
import axios from 'axios'
import router from 'vue-router'
export default {
data() {
return {
errors: [],
}
},
methods: {
onSubmit: function () {
axios.post('/users', {
user: {
email: this.$refs.user_email.value,
password: this.$refs.user_password.value
}
})
.then(response => {
})
.catch(error => {
this.errors.push(error.response.data.errors);
})
}
}
}
</script>
It basically collects errors that come back from the server. Those are the error messages I want to show if something goes wrong.
EXAMPLE:
If the email is blank, this will capture the "email_is_blank" message with the errors array. But how can I take that message and display it in the form using Vue.js and Vuetify?

Codepen example
One of the ways would be to create object with value and error string:
data: () => ({
email: {
value: '',
error: ''
}
})
Then bind your model to object value, and error-messages prop to object error:
<v-text-field
v-model="email.value"
label="email"
:error-messages="email.error"
></v-text-field>
In your response just change the value of error:
...
.then(response => {
this.email.error = response.errors.email // or whatever the path is to the relevant error message from the server
})

Related

How can I call the edit component within another file?

I have a project and this project is for the owners of the purchase for the purchase of cars and many other operations, but I have a table with several columns, and within these columns there is a column I listen to action and this column contains a button called Edit and I want when I click on the Edit button to be used The component of the modification within this file, how can I do this?
And it is the Edit file in which the Edit form is located.
Edit.vue:
<template>
<v-row justify="center">
<v-dialog v-model="editDialog" persistent max-width="1050px" height="400px">
<template v-slot:activator="{ on, attrs }">
<v-btn
fab
accent
class="grey lighten-1 margin pa-4"
dark
v-bind="attrs"
v-on="on"
>
<v-icon>
mdi-pencil
</v-icon>
</v-btn>
</template>
<v-card>
<v-layout>
<v-flex xs12>
<div class="myfont pl-5">
<v-card-title>
<span> Edit Car</span>
</v-card-title>
</div>
</v-flex>
</v-layout>
<v-divider xs12></v-divider>
<v-layout>
<v-flex xs12>
<v-card-text>
<v-container>
<v-row>
<v-col cols="12">
<v-text-field
name="name"
label="Name"
id="name"
class="colorLabel"
v-model="editedName"
multi-line
required
></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field
name="priceOfSale"
label="Price Of Sale"
id="priceOfSale"
v-model="editedPrice"
class="colorLabel"
multi-line
required
></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field
name="numberOfSeats"
label="NumberOfSeats"
id="numberOfSeats"
v-model="editedNumberOfSeats"
multi-line
required
></v-text-field>
</v-col>
</v-row>
</v-container>
</v-card-text>
</v-flex>
</v-layout>
<v-divider></v-divider>
<v-layout>
<v-flex xs12>
<v-card-actions>
<v-btn class="myfont pl-5 text-right" text #click="onSaveChanges">
Save
</v-btn>
<v-btn
class="myfont pl-5 text-center"
text
#click="editDialog = false"
>
Cancel
</v-btn>
</v-card-actions>
</v-flex>
</v-layout>
</v-card>
</v-dialog>
</v-row>
</template>
<script>
import { mapActions } from "vuex";
import ActionsTypes from "../store/types/actions-types";
export default {
props: ["car"],
data() {
return {
editedName: this.car.name,
editedPrice: this.car.price,
editedNumberOfSeats: this.car.seatsNumber,
};
},
methods: {
...mapActions({
editCarInformations: ActionsTypes.EDIT_CAR_ACTION,
}),
onSaveChanges() {
const UpdatedCar = { ...this.car };
UpdatedCar.name = this.editedName;
UpdatedCar.price = this.editedPrice;
UpdatedCar.seatsNumber = this.editedNumberOfSeats;
this.editCarInformations(UpdatedCar);
},
},
};
</script>
This file, in which there is a table containing several columns, and the last column is Action, which contains the Modify button, the Modify button, and when I press it, the universe of the amendment is called.
viewAllCars:
<template>
<v-app class="bg">
<v-container>
<v-card
class="mx-auto mt-5 pa-3"
max-width="100%"
id="limited-products"
:style="'border: 0px solid #D50000;'"
>
<v-btn class="red accent-4 color myfont pl-3" #click="onCreateCar">
evict Cashig
</v-btn>
<v-data-table
:headers="tableHeaders"
:items="loadedCarsGetter"
:page.sync="page"
:items-per-page="itemsPerPage"
hide-default-footer
class="elevation-1"
#page-count="pageCount = $event"
>
<template #[`item.actions`]="{ item }">
<v-btn icon #click="edit(item.id)">
<v-icon>mdi-pencil</v-icon>
</v-btn>
<v-btn icon #click="delete (item.id)">
<v-icon>mdi-delete</v-icon>
</v-btn>
</template>
</v-data-table>
<!-- pagination -->
<div class="text-center pt-2">
<v-pagination v-model="page" :length="pageCount"></v-pagination>
<v-text-field
:value="itemsPerPage"
label="Items per page"
type="number"
min="-1"
max="15"
#input="itemsPerPage = parseInt($event, 10)"
class="pl-7 pr-7"
></v-text-field>
</div>
</v-card>
</v-container>
</v-app>
</template>
<script>
import { mapActions, mapGetters } from "vuex";
import ActionsTypes from "../../store/types/actions-types";
import GettersTypes from "../../store/types/getters-types";
export default {
data() {
return {
page: 1,
pageCount: 0,
itemsPerPage: 10
};
},
created() {},
computed: {
...mapGetters({
loadedCarsGetter: GettersTypes.GET_CAR_FORM_GETTER,
tableHeaders: GettersTypes.GET_HEADERS_TABLE_GETTER,
}),
},
mounted() {
// this.loadedCarsGetter();
this.loadedCarsAction();
},
methods: {
...mapActions({
editcardispatcher: ActionsTypes.EDIT_CAR_ACTION,
deletecardispatcher: ActionsTypes.DELETE_CAR_ACTION,
loadedCarsAction: ActionsTypes.GET_ALL_CAR_ACTION
}),
edit() {
this.editcardispatcher({});
},
delete(){
this.deletecardispatcher(
this.car.id
)
}
},
};
</script>
First of all, you don't need the "v-row" in the Edit.vue. Remove it.
As you have the button as the activator, you should just add the component as Avraham mentioned. But you need to know that there are some caveats with this approach
This is gonna be increasing the memory usage by the browser. As a separate instance of Edit.vue will be added to the DOM for each row in your table.
Each Edit.vue instance will preserve the data in it with the changes that the user might make. And you'll have to handle the data resets.
A better solution would be to add only one instance of Edit.vue and add/remove the component from the DOM using a v-if.
This will keep your table using one instance of Edit.vue, and the addition and removal of the component will handle the data reset.
Something like this
In the file that contains the v-data-table, update the template as follows
<template>
......
<v-data-table ...>
...
<template #[`item.actions`]="{ item }">
<v-btn icon #click="edit(item.id)">
<v-icon>mdi-pencil</v-icon>
</v-btn>
<v-btn icon #click="delete(item.id)">
<v-icon>mdi-delete</v-icon>
</v-btn>
</template>
...
</v-data-table>
<edit :car="item" v-if="showEditDialog = true" #closed="showEditDialog = false" />
......
</template>
<script>
import Edit from 'Edit.vue'
export default {
components: { Edit },
data: () =({
item: {},
showEditDialog: false,
}),
methods: {
edit(item) {
this.item = item
this.showEditDialog = true
}
}
}
</script>
In your Edit.vue, add a watcher for the "editDialog" property to emit an event to remove the edit dialog from the DOM.
watch: {
editDialog(val){
if(!val)
this.$emit('closed')
}
}
And remove this part from the Edit.Vue
<template v-slot:activator="{ on, attrs }">
<v-btn
fab
accent
class="grey lighten-1 margin pa-4"
dark
v-bind="attrs"
v-on="on"
>
<v-icon>
mdi-pencil
</v-icon>
</v-btn>
</template>
Good luck.
You should import the Edit.vue component in the car viewer component and use it instead of the edit button:
...
<template #[`item.actions`]="{ item }">
<!-- Pass the item to the `car` prop -->
<edit :car="item" />
<v-btn icon #click="delete (item.id)">
<v-icon>mdi-delete</v-icon>
</v-btn>
</template>
...
<script>
import Edit from 'Edit.vue' // make sure the path to the component is correct
export default {
components: { Edit },
...
};
</script>

Store the Data in Local Storage

I have this Component to create a recipe, but I want to store the data in the local storage.
When I print the "recipeData" it is printed on the console.
For example, I enter the name of the recipe and the description for it, etc. and I print the data in the console and the elements I have entered are printed, but when I print "recipeData.title" it is printed that it is not defined.
And when I want to store the recipeData in the local storage, it does not store it.
How can i solve the problem?
<template>
<v-app>
<v-container>
<v-layout row>
<v-flex xs12 sm6 offset-sm3>
<h2 class="btn-style">Create Recipe</h2>
</v-flex>
</v-layout>
<v-layout row>
<v-flex xs12>
<form #submit.prevent="onCreateRecipe">
<v-layout>
<v-flex xs12 sm6 offset-sm3>
<v-text-field
name="title"
label="Title"
id="title"
v-model="title"
color="#43A047"
required
>
</v-text-field>
</v-flex>
</v-layout>
<v-layout>
<v-flex xs12 sm6 offset-sm3>
<v-text-field
name="imageUrl"
label="ImageUrl"
id="imageUrl"
v-model="imageUrl"
color="#43A047"
required
>
</v-text-field>
</v-flex>
</v-layout>
<v-layout>
<v-flex xs12 sm6 offset-sm3>
<img :src="imageUrl" height="300px" />
</v-flex>
</v-layout>
<v-layout>
<v-flex xs12 sm6 offset-sm3>
<v-text-field
name="description"
label="Description"
id="description"
v-model="description"
color="#43A047"
multi-line
required
>
</v-text-field>
</v-flex>
</v-layout>
<v-layout>
<v-flex xs12 sm6 offset-sm3>
<v-text-field
name="ingredients"
label="Ingredients"
id="ingredients"
v-model="ingredients"
color="#43A047"
multi-line
required
>
</v-text-field>
</v-flex>
</v-layout>
<v-layout row>
<v-flex xs12 sm6 offset-sm3>
<v-btn
class="green darken-1 color"
:disabled="!formIsValid"
type="submit"
>
Create Redcipe
</v-btn>
</v-flex>
</v-layout>
</form>
</v-flex>
</v-layout>
</v-container>
</v-app>
</template>
<script>
export default {
data() {
return {
title: "",
imageUrl: "",
description: "",
ingredients: "",
};
},
computed: {
formIsValid() {
return (
this.title !== "" &&
this.description !== "" &&
this.imageUrl !== "" &&
this.ingredients != ""
);
},
},
methods: {
onCreateRecipe() {
if (!this.formIsValid) {
return;
}
const recipeData = {
title: this.title,
description: this.description,
imageUrl: this.imageUrl,
ingredients: this.ingredients,
};
console.log(recipeData)
console.log("The Local Storage"+localStorage.setItem(this.recipeData));
}
}
};
</script>
<style scoped>
.btn-style {
color: #43a047;
}
.color {
color: #fafafa;
}
</style>
to store the data to localStorage, you have to define name of the storage first and then the data.
localStorage.setItem('storageName', recipeData)
to see the data of localStorage.
console.log(localStorage.getItem('StorageName'))
learn more https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
and theres no need this.recipeData because u declare the variable on the function. to get the data enought with recipeData
Local storage is a key-value data structure, you have to specify a key that will be used to retrieved the data from the storage as the first parameter. This is the syntax localStorage.setItem(keyName, keyValue);
Also, it has to be saved and retrieved as a string, stringifying it when saving and parsing it when getting it.
Your code for saving the object should be like this:
localStorage.setItem('recipeDataKey', JSON.stringify(this.recipeData));
and for retrieving:
const recipeData = JSON.parse(localStorage.setItem('recipeDataKey'));

Why parameter returns 'undefined' when using Vuetify

I'm starting to use vuetify along with a 'payment gateway' that I'm learning, but I've had a little problem.
If I have a form like this:
<form #submit.prevent="continuar" id="customer-form">
<div class="card-errors"></div>
<div class="form-group">
<label>Nombre del usuario de tarjeta</label>
<input type="text" data-epayco="card[name]">
</div>
<div class="form-group">
<label>Email</label>
<input type="text" data-epayco="card[email]">
</div>
...
<button type="submit">¡Pagar ahora!</button>
</form>
The "Token" parameter returns a value that is not undefined.
continuar(event){
ePayco.token.create(event.target, (error, token) => {
if(!error) {
console.log("token: " + token)
} else {
console.log(error)
}
})
},
But when I use vuetify the "Token" parameter returns "undefined" even when the "Epayco" library shows a message that everything has happened correctly.
<form id="customer-form" #submit.prevent="continuar">
<div class="card-errors"></div>
<v-layout row align-center>
<v-flex md3 offset-md1 class="mr-3">
<v-layout justify-end>
<span>Nombre en la tarjeta*</span>
</v-layout>
</v-flex>
<v-flex md4>
<v-text-field data-epayco="card[name]"/>
</v-flex>
</v-layout>
<v-layout row align-center>
<v-flex md3 offset-md1 class="mr-3">
<v-layout justify-end>
<span>Email</span>
</v-layout>
</v-flex>
<v-flex md4>
<v-text-field data-epayco="card[email]"/>
</v-flex>
</v-layout>
......
<v-layout class="my-3" justify-center>
<v-btn type="submit">Pagar</v-btn>
</v-layout>
</form>
Does anyone know why the problem?
It should be noted that when an error occurs the parameter 'error' returns the error and not undefined
According to the example here it appears you should be using the ePayco.token.create() function a bit differently.
epayco.token.create(paymentDetails)
.then(function(token) {
console.log(token);
})
.catch(function(err) {
console.log("err: " + err);
});

Cannot read property 'getHours' of null

I am following this vue/vuex/firebase tutorial https://www.youtube.com/watch?v=fls6uX7WWSs&list=PL55RiY5tL51qxUbODJG9cgrsVd7ZHbPrt&index=21 ,and I am currently stuck with the vuetify date and time pickers. I keep getting this error [Vue warn]: Error in render: "TypeError: Cannot read property 'getHours' of null"
I have tried several solutions posted here and other places like using .toISOString().substr(0, 10) to convert the date and time to a string and i get this error [Vue warn]: Error in render: "TypeError: Cannot read property '1' of null"
<template>
<v-container>
<v-layout row>
<v-flex xs12 sm6 offset-sm3>
<h4>Create a new Meetup</h4>
</v-flex>
</v-layout>
<v-layout row>
<v-flex xs12>
<form #submit.prevent="onCreateMeetup">
<v-layout row>
<v-flex xs12 sm6 offset-sm3>
<v-text-field name="title" label="Title" id="title" v-model="title" required></v-text-field>
</v-flex>
</v-layout>
<v-layout row>
<v-flex xs12 sm6 offset-sm3>
<v-text-field
name="location"
label="Location"
id="location"
v-model="location"
required
></v-text-field>
</v-flex>
</v-layout>
<v-layout row>
<v-flex xs12 sm6 offset-sm3>
<v-btn raised class="primary" #click="onPickFile">Upload Image</v-btn>
<input
type="file"
style="display: none"
ref="fileInput"
accept="image/*"
#change="onFilePicked"
>
</v-flex>
</v-layout>
<v-layout row>
<v-flex xs12 sm6 offset-sm3>
<img :src="imageUrl" height="150">
</v-flex>
</v-layout>
<v-layout row>
<v-flex xs12 sm6 offset-sm3>
<v-textarea
name="description"
label="Description"
id="description"
v-model="description"
required
></v-textarea>
</v-flex>
</v-layout>
<v-layout row>
<v-flex xs12 sm6 offset-sm3>
<h4>Choose a Data & Time</h4>
</v-flex>
</v-layout>
<v-layout row class="mb-2">
<v-flex xs12 sm6 offset-sm3>
<v-date-picker color="primary" v-model="date"></v-date-picker>
<p>{{date}}</p>
</v-flex>
</v-layout>
<v-layout row>
<v-flex xs12 sm6 offset-sm3>
<v-time-picker color="primary" v-model="time"></v-time-picker>
<p>{{time}}</p>
</v-flex>
</v-layout>
<v-layout row>
<v-flex xs12 sm6 offset-sm3>
<v-btn class="primary" :disabled="!formIsValid" type="submit">Create Meetup</v-btn>
<p>{{submittableDateTime}}</p>
</v-flex>
</v-layout>
</form>
</v-flex>
</v-layout>
</v-container>
</template>
<script>
export default {
data() {
return {
title: "",
location: "",
imageUrl: "",
description: "",
date: new Date().toISOString().substr(0, 10),
time: new Date().toISOString().substr(0, 10),
image: null
}
},
computed: {
formIsValid() {
return (
this.title !== "" &&
this.location !== "" &&
this.imageUrl !== "" &&
this.description !== ""
)
},
submittableDateTime() {
const date = new Date(this.date)
if (typeof this.time === "string") {
let hours = this.time.match(/^(\d+)/)[1]
const minutes = this.time.match(/:(\d+)/)[1]
date.setHours(hours)
date.setMinutes(minutes)
} else {
date.setHours(this.time.getHours())
date.setMinutes(this.time.getMinutes())
}
return date
}
},
methods: {
onCreateMeetup() {
if (!this.formIsValid) {
return
}
if (!this.image) {
return
}
const meetupData = {
title: this.title,
location: this.location,
image: this.image,
description: this.description,
date: this.submittableDateTime
}
this.$store.dispatch("createMeetup", meetupData)
this.$router.push("/meetups")
},
onPickFile() {
this.$refs.fileInput.click()
},
onFilePicked(event) {
const files = event.target.files
let filename = files[0].name
if (filename.lastIndexOf(".") <= 0) {
return alert("Please add a valid file!")
}
const fileReader = new FileReader()
fileReader.addEventListener("load", () => {
this.imageUrl = fileReader.result
})
fileReader.readAsDataURL(files[0])
this.image = files[0]
}
}
}
</script>
I want a format that will be submit-able to firebase like 2019-02-18T23:20:38.950Z that comes from the submittableDateTime computed function. Thanks for the halp!
You can't pass a string like 2019-01-01 to v-time-picker. It should be a Date object
You can change your init data
data() {
return {
title: "",
location: "",
imageUrl: "",
description: "",
date: new Date().toISOString().substr(0, 10),
time: new Date(),
image: null
}
}

wrapper.find is not working for avoriaz

I am using avoriaz for testing in vuejs . Here is my code for testing and components.
<template>
<v-container fluid class="login-container">
<div class="hyperlogo">
<center>
<img src='../../../assets/logo.png' height="100" width="100">
<p>HyperEmail</p>
<a>v0.11.1</a>
</center>
</div>
<v-layout row wrap>
<v-flex xs6 md4 offset-md4>
<v-card class="login-card">
<v-card-text class="login-text">
<v-text-field
v-on:focus="focusUser"
label="Username"
placeholder="Username"
class="mt-5"
v-model="username"
#keyup.enter.native="validateData"
v-bind:rules="[userNameError]"
></v-text-field>
<v-text-field
v-on:focus="focusPassword"
label="Password"
placeholder="Password"
v-model="password"
type="password"
v-on:keyup.enter.native="validateData"
v-bind:rules="[passwordError]"
></v-text-field>
</v-card-text>
<div #click="validateData" class="button-style">
<v-btn block primary light >Login</v-btn>
</div>
</v-card>
</v-flex>
<Error v-if="error" :text="error.msg" :onDestroy="resetError" />
</v-layout>
</v-container>
</template>
For testing
import Login from '#/containers/views/login/Login.vue'
describe('Login.vue', () => {
it('checks text inside login component', () => {
const wrapper = mount(Login)
// let parent = wrapper.find('.hyperlogo')[0]
// expect(wrapper.contains('.hyperlogo')).to.equal(true)
if (wrapper.find('.hyperlogo')[0]) {
console.log('Yes it is there')
}
expect(wrapper.text()).to.equal('')
expect(wrapper.find('center')[0].is('center')).to.equal(true)
})
})
Here wrapper.find is not working
Error : - TypeError: Cannot read property 'is' of undefined .. Can anyone helps me to solve this type of error ??