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'));
Related
I have a project to add an ingredient in a recipe to "Shopping List".
And in this picture there are several ingredients at the bottom "sugar".
When I click on the plus sign I want to add the ingredient to the "shopping List".
How can I solve this problem?
In this file, you create an ingredient with this ingredient's data such as its name and quantity.
CreateIngredient:
<template>
<v-app>
<v-container>
<v-layout row>
<v-flex xs12 sm6 offset-sm3>
<h2 class="btn-style">Add Ingredient</h2>
</v-flex>
</v-layout>
<v-layout row>
<v-flex xs12>
<form #submit.prevent="onAddIngredient">
<v-layout>
<v-flex xs12 sm6 offset-sm3>
<v-text-field name="id" label="Id" id="id" v-model="id" color="#43A047" required>
</v-text-field>
</v-flex>
</v-layout>
<v-layout>
<v-flex xs12 sm6 offset-sm3>
<v-text-field name="name" label="Name" id="name" v-model="Name" color="#43A047" required>
</v-text-field>
</v-flex>
</v-layout>
<v-layout>
<v-flex xs12 sm6 offset-sm3>
<v-text-field name="ingredientsQuantity" label="Ingredients Quantity" id="ingredientsQuantity" v-model="Quantity" 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" type="submit">
Add
</v-btn>
</v-flex>
</v-layout>
</form>
</v-flex>
</v-layout>
</v-container>
</v-app>
</template>
<script>
export default {
data() {
return {
id: "",
Name: "",
Quantity: "",
};
},
computed: {
formIsValid() {
return (
this.id !== "" &&
this.Name !== "" &&
this.Quantity !== ""
);
}
},
methods: {
onAddIngredient() {
if (!this.formIsValid) {
return;
}
const ingredientData = {
id: this.id,
Name: this.Name,
Quantity: this.Quantity
};
this.$store.commit("createIngredients", ingredientData);
const stringifiedData = JSON.stringify(ingredientData);
// console.log("S: ", stringifiedData);
localStorage.setItem("ingredient", stringifiedData);
console.log("We got : ", JSON.parse(localStorage.getItem("ingredient")));
}
},
};
</script>
<style scoped>
.btn-style {
color: #43a047;
}
.color {
color: #fafafa;
}
</style>
And this file that displays all the ingredients
shoppingList:
<template>
<div>
<v-container class="mb-30">
<v-flex class="floating-right">
<v-btn large router to="/CreateNewIngrediets" class="green darken-1 btn-style margine mr-50">
<v-icon class="green darken-1 btn-style">mdi-plus</v-icon>
</v-btn>
</v-flex>
<v-container>
<v-layout row wrap v-for="ingredient in ingredients" :key="ingredient.id" class="mb-3 mt-4">
<v-flex xs6 sm8 md4 offset-sm1 offset-md2>
<v-card class="grey lighten-4 pl-3 ">
<v-container fluid>
<v-layout row class="pl-14">
<v-flex xs7 sm8 md9>
<v-card-title primary-title>
<v-flex xs7 sm8 md9>
<div>
{{ ingredient.Name }}
</div>
</v-flex>
<v-flex xs7 sm8 md9>
<div>
{{ ingredient.Quantity }}
</div>
</v-flex>
<v-flex xs5 sm4 md2>
<v-btn class="deleteColorIcon">
<v-icon left class=" pl-4" #click="
$store.commit('delete_ingredient', ingredient.id)
">
mdi-delete
</v-icon>
<!-- </v-btn> -->
</v-btn>
</v-flex>
</v-card-title>
</v-flex>
</v-layout>
</v-container>
</v-card>
</v-flex>
</v-layout>
</v-container>
</v-container>
</div>
</template>
<script>
export default {
computed: {
Recipes() {
return this.$store.getters.loadedRecipes;
},
ingredients() {
return this.$store.getters.loadedingredients;
}
}
};
</script>
<style scoped>
.color {
color: #43a047;
}
.deleteColorIcon {
color: #e53935;
}
.btn-style {
color: aliceblue;
}
</style>
And this file that displays the recipe as the image above, and through it, all the recipe elements, including the ingredients, are displayed.
Recipe:
<template>
<v-container>
<v-layout row wrap>
<v-flex x12>
<v-card>
<!-- <v-card-title> -->
<v-card-text>
<h4 class="btn-style mt-4 mb-4 font">
{{ recipe.title }}
</h4>
<v-img height="530px" :src="recipe.imageUrl" class="mb-4"></v-img>
<div class="btn-style mb-6">
{{ recipe.description }}
</div>
<div v-for="ing in recipe.ingredients" :key="ing.id">
{{ ing.Name }} {{ ing.Quantity }}
<v-btn class="green darken-1 color mb-5 ml-4 mr-4 pl-50">
<v-icon class="green darken-1 btn-style">mdi-plus</v-icon>
</v-btn>
</div>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<!-- <v-flex xs5 sm4 md2> -->
<v-btn class=" mb-4 mr-4">
<v-icon left class=" pl-4 ">
mdi-pen
</v-icon>
<!-- </v-btn> -->
</v-btn>
<!-- </v-flex> -->
</v-card-actions>
</v-card>
</v-flex>
</v-layout>
</v-container>
</template>
<script>
import {
mapGetters
}
from "vuex";
export default {
props: ["id"],
computed: {
recipe() {
const loadedRecipe = this.$store.getters.loadedRecipe(this.id);
console.log("We loaded a recipe with value : ", loadedRecipe);
return loadedRecipe;
},
ingredient() {
return this.$store.getters.loadedingredient(this.id);
}
},
methods: {
}
};
</script>
<style scoped>
.btn-style {
color: #43a047;
}
.color {
color: #fafafa;
}
.deleteColorIcon {
color: #e53935;
}
.font {
font-size: 30px;
}
</style>
You can put a on click observer un your button, define a function that would execute in such case and pass it the object you want to store like so:
<div v-for="ing in recipe.ingredients" :key="ing.id">
{{ ing.Name }} {{ ing.Quantity }}
<v-btn class="green darken-1 color mb-5 ml-4 mr-4 pl-50" #click="addToShoppingList(ing)">
<v-icon class="green darken-1 btn-style">mdi-plus</v-icon>
</v-btn>
</div>
Then in vuex, store it in the state using an action and a mutator:
export default new Vuex.Store({
state: {
shoppingList: []
},
getters: {
getShoppingList(state) { return state.shoppingList }
},
mutations: {
ADD_ITEM_TO_SHOPPING_LIST(state, ingParam) {
state.shoppingList.push(ingParam);
}
}
,
actions: {
addItemToShoppingList({commit}, ingParam) {
commit('ADD_ITEM_TO_SHOPPING_LIST', ingParam);
}
}
And last thing in your methods like so:
export default {
methods: {
addToShoppingList(ingParam) {
this.$store.dispatch('addItemToShoppingList', ingParam);
}
}
}
Hope it helps ;)
This is a project for running a site that displays recipes and performs several operations such as creating a site, but I encountered this problem.
I have this file to create a recipe where I enter the recipe data, but how can I create an alert on this page says that creating a recipe succeeded.
How can I solve the Problem?
The data is in this file.
createRecipe.vue:
<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="id"
label="Id"
id="id"
v-model="id"
color="#43A047"
required
>
</v-text-field>
</v-flex>
</v-layout>
<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="ingredientsName"
label="IngredientsName"
id="ingredientsName"
v-model="ingredientsName"
color="#43A047"
multi-line
required
>
</v-text-field>
</v-flex>
</v-layout>
<v-layout>
<v-flex xs12 sm6 offset-sm3>
<v-text-field
name="ingredientsQuantity"
label="IngredientsQuantity"
id="ingredientsQuantity"
v-model="ingredientsQuantity"
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 Recipe
</v-btn>
</v-flex>
</v-layout>
</form>
</v-flex>
</v-layout>
</v-container>
</v-app>
</template>
<script>
export default {
data() {
return {
id:"",
title: "",
imageUrl: "",
description: "",
ingredientsName: "",
ingredientsQuantity: "",
};
},
computed: {
formIsValid() {
return (
this.id !== ""&&
this.title !== "" &&
this.description !== "" &&
this.imageUrl !== "" &&
this.ingredientsName !== ""&&
this.ingredientsQuantity !== ""
);
}
},
methods: {
onCreateRecipe() {
if (!this.formIsValid) {
return;
}
const recipeData = {
id:this.id,
title: this.title,
description: this.description,
imageUrl: this.imageUrl,
ingredientsName: this.ingredientsName,
ingredientsQuantity: this.ingredientsQuantity
};
// Here we call the setter to put the Data inside it
this.$store.commit('createRecipe', recipeData)
const stringifiedData = JSON.stringify(recipeData);
// console.log("S: ", stringifiedData);
localStorage.setItem("recipe", stringifiedData);
console.log("We got : ", JSON.parse(localStorage.getItem("recipe")));
},
}
};
</script>
<style scoped>
.btn-style {
color: #43a047;
}
.color {
color: #fafafa;
}
</style>
You need to use async action not mutation you can not use async operations with mutations. It should look like this:
this.$store.dispatch('createRecipe', recipeData).then(() => //alert('STH') or whatever you want to do after you add recipe)
editItem(){
if(this.selected.length!=1) {
alert("Choose only one row");
} else {
//what should i put in here?
}
}
Hi, I created a datatable with selectboxs for each row using vuejs.
I created a button that when clicked activates the method above, how can I make it that when just one row is selected a form is opened and you can edit the info in that row?
I made a form that when filled and a button "addItem" is clicked the data-table will insert the row with the info in the form.
I tried using v-edit-dialog but i just cant make it work...
here is the whole code:
<style>
table th + th { border-left:1px solid #dddddd; }
table td + td { border-left:1px solid #dddddd; }
</style>
<template>
<div id="consulta">
<v-container grid-list-xl fluid>
<v-layout row wrap>
<v-flex sm12>
<v-widget title="Testando">
<div slot="widget-content">
<v-container id="teste">
<v-layout row>
<v-flex xs4>
<v-subheader>EAN</v-subheader>
</v-flex>
<v-flex xs8>
<v-text-field
name="ean"
label=""
color="primary"
id="ean"
mask="#.######.######"
></v-text-field>
</v-flex>
</v-layout>
<v-layout row>
<v-flex xs4>
<v-subheader>Descrição</v-subheader>
</v-flex>
<v-flex xs8>
<v-text-field
name="desc"
id="desc"
label=""
value=""
:rules="[(v) => v.length <= 50 || 'Max 50 characters']"
:counter="50"
single-line
></v-text-field>
</v-flex>
</v-layout>
<v-layout row>
<v-flex xs4>
<v-subheader>Marca</v-subheader>
</v-flex>
<v-flex xs8>
<v-text-field
name="marca"
label=""
color="primary"
id="marca"
></v-text-field>
</v-flex>
</v-layout>
<v-layout row>
<v-flex xs4>
<v-subheader>Departamento</v-subheader>
</v-flex>
<v-flex xs8>
<v-text-field
name="dep"
label=""
color="primary"
id="dep"
></v-text-field>
</v-flex>
</v-layout>
<v-layout row>
<v-flex xs4>
<v-subheader>Categoria</v-subheader>
</v-flex>
<v-flex xs8>
<v-text-field
name="categ"
label=""
color="primary"
id="categ"
></v-text-field>
</v-flex>
</v-layout>
<v-layout row>
<v-flex xs4>
<v-subheader>Atributos</v-subheader>
</v-flex>
<v-flex xs8>
<v-text-field
name="att"
label=""
color="primary"
id="att"
></v-text-field>
</v-flex>
</v-layout>
<v-layout row>
<v-flex xs4>
<v-subheader>Loja</v-subheader>
</v-flex>
<v-flex xs8>
<v-text-field
name="loja"
label=""
color="primary"
id="loja"
></v-text-field>
</v-flex>
</v-layout>
<v-layout row>
<v-flex xs4>
<v-subheader>URL</v-subheader>
</v-flex>
<v-flex xs8>
<v-text-field
name="url"
label=""
color="primary"
id="url"
></v-text-field>
</v-flex>
</v-layout>
<div>
<v-btn small color="primary" #click="addItem">Enviar</v-btn>
</div>
</v-container>
</div>
</v-widget>
</v-flex>
<v-flex sm12>
<h1>Tabela</h1>
</v-flex>
<v-flex lg12>
<v-toolbar card color="white">
<v-text-field
flat
solo
prepend-icon="search"
placeholder="Type something"
v-model="search"
hide-details
class="hidden-sm-and-down"
></v-text-field>
<v-btn icon>
<v-icon>filter_list</v-icon>
</v-btn>
</v-toolbar>
<v-divider></v-divider>
<v-card>
<v-card-text class="pa-0">
<v-data-table
:headers="headers"
:search="search"
:items="items"
item-key="ean"
class="elevation-1"
select-all
v-model="selected">
<template slot="items" slot-scope="props">
<td> <v-checkbox v-model="props.selected" primary hide-details></v-checkbox></td>
<td class="text-xs-center">{{ props.item.ean }}</td>
<td class="text-xs-center">{{ props.item.desc }}</td>
<td class="text-xs-center">{{ props.item.marca }}</td>
<td class="text-xs-center">{{ props.item.dep }}</td>
<td class="text-xs-center">{{ props.item.categ }}</td>
<td class="text-xs-center">{{ props.item.att }}</td>
<td class="text-xs-center">{{ props.item.loja }}</td>
<td class="text-xs-center">{{ props.item.url }}</td>
</template>
</v-data-table>
</v-card-text>
</v-card>
<div>
<v-btn color="red" #click="deleteItem">Delete</v-btn>
<v-btn color="orange" #click="editItem">Edit</v-btn>
</div>
</v-flex>
</v-layout>
</v-container>
</div>
</template>
<script>
import API from '#/api';
import EChart from '#/components/chart/echart';
import MiniStatistic from '#/components/widgets/statistic/MiniStatistic';
import PostListCard from '#/components/widgets/card/PostListCard';
import ProfileCard from '#/components/widgets/card/ProfileCard';
import PostSingleCard from '#/components/widgets/card/PostSingleCard';
import WeatherCard from '#/components/widgets/card/WeatherCard';
import PlainTable from '#/components/widgets/list/PlainTable';
import PlainTableOrder from '#/components/widgets/list/PlainTableOrder';
import VWidget from '#/components/VWidget';
import Material from 'vuetify/es5/util/colors';
import VCircle from '#/components/circle/VCircle';
import BoxChart from '#/components/widgets/chart/BoxChart';
import ChatWindow from '#/components/chat/ChatWindow';
import CircleStatistic from '#/components/widgets/statistic/CircleStatistic';
import LinearStatistic from '#/components/widgets/statistic/LinearStatistic';
export default {
layout: 'dashboard',
components: {
VWidget,
MiniStatistic,
ChatWindow,
VCircle,
WeatherCard,
PostSingleCard,
PostListCard,
ProfileCard,
EChart,
BoxChart,
CircleStatistic,
LinearStatistic,
PlainTable,
PlainTableOrder
},
data() {
return{
search: '',
selected: [],
headers:[
{text:'EAN', value:'ean', sortable: true, align:"center"},
{text:'Descrição', value:'desc', sortable: true, align:"center"},
{text:'Marca', value:'marca', sortable: true, align:"center"},
{text:'Departamento', value:'dep', sortable: true, align:"center"},
{text:'Categoria', value:'categ', sortable: true, align:"center"},
{text:'Atributos', value:'att', sortable: true, align:"center"},
{text:'Loja', value:'loja', sortable: true, align:"center"},
{text:'URL', value:'url', sortable: true, align:"center"},
],
items:[{
ean: '9234858583',
desc: 'Item teste',
marca: 'Danone',
dep: 'Comida?',
categ: 'Laticinio',
att: 'Yogurt',
loja: 'Mercadinho',
url: 'www.mercadinho.fakeaddress.com',
}],
}
},
methods: {
addItem(){
if(document.querySelector("#ean").value!=""&&
document.querySelector("#desc").value!=""&&
document.querySelector("#marca").value!=""&&
document.querySelector("#dep").value!=""&&
document.querySelector("#categ").value!=""&&
document.querySelector("#att").value!=""&&
document.querySelector("#loja").value!=""&&
document.querySelector("#url").value!=""){
let registro = {
"ean": document.querySelector("#ean").value,
"desc": document.querySelector("#desc").value,
"marca":document.querySelector("#marca").value,
"dep": document.querySelector("#dep").value,
"categ": document.querySelector("#categ").value,
"att": document.querySelector("#att").value,
"loja": document.querySelector("#loja").value,
"url": document.querySelector("#url").value,
};
this.insert(registro);
this.items.push(registro)
}
else{
alert("Um ou mais campos não foram preenchidos!");
}
},
editItem(){
if(this.selected.length){
alert("Select just one row")
}
else{
//show form to edit the info
}
},
deleteItem(){
if(confirm("Deseja deletar tais fileiras?")){
for(var i = 0; i < this.selected.length; i++){
const index = this.items.indexOf(this.selected[i]);
this.items.splice(index, 1);
}
}
}
}
}
</script>
Since you're using Vuetify as your Material Design Framework you could use the Dialog Component. You can find many Example in the official documentation: https://vuetifyjs.com/en/components/dialogs#form
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
}
}
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
})