Cannot read property 'getHours' of null - vuejs2

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
}
}

Related

add ingredient to shopping List

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 ;)

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'));

create alert in Recipe Component

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)

Unknown custom element: <x-flex> - did you register the component correctly? For recursive components, make sure to provide the "name" option

How Do I resolve this. I am getting the error Unknown custom element: - did you register the component correctly? For recursive components, make sure to provide the "name" option. This error is being printed on the console as I run the app. When it does this , the nothing is displayed on on the views its just blank.
<template>
<v-container id="dashboard" fluid grid-list-lg class="mx- pa-7">
<v-layout row wrap>
<v-flex md3 sm6 xs12>
<v-card class="cyan darken-3" l light>
<v-container fluid grid-list-sm light>
<v-layout class="mt-0 mb-0" row wrap>
<v-flex d-flex xs3>
<v-icon class="mx-0" x-large light>beenhere</v-icon>
</v-flex>
<v-flex d-flex xs9 >
<v-layout class="mt-2 mb-0 pa-0" row wrap>
<x-flex d-flex xs12>
<div class="silver--text subheading">Income Revenue</div>
</x-flex>
<v-flex d-flex xs12>
<div class="silver--text display-1">{{totalUsers}}</div>
<v-btn outline class="darkgrey--text darken-1" right flat small>More</v-btn>
</v-flex>
</v-layout>
</v-flex>
</v-layout>
</v-container>
</v-card>
</v-flex>
<v-flex md3 sm6 xs12>
<v-card class="blue-grey darken-1">
<v-container fluid grid-list-sm light>
<v-layout class="mt-0 mb-0" row wrap>
<v-flex d-flex xs3>
<v-icon class="mx-0" x-large light>beenhere</v-icon>
</v-flex>
<v-flex d-flex xs9 >
<v-layout class="mt-2 mb-0 pa-0" row wrap>
<x-flex d-flex xs12>
<div class="silver--text subheading">Today's Revenue</div>
</x-flex>
<v-flex d-flex xs12>
<div class="silver--text display-1">{{totalAmount}}</div>
<v-btn outline class="darkgrey--text darken-1" right flat small>More</v-btn>
</v-flex>
</v-layout>
</v-flex>
</v-layout>
</v-container>
</v-card>
</v-flex>
<v-flex md3 sm6 xs12>
<v-card class="pink lighten-4">
<v-container fluid grid-list-sm light>
<v-layout class="mt-0 mb-0" row wrap>
<v-flex d-flex xs3>
<v-icon class="mx-0" x-large light>beenhere</v-icon>
</v-flex>
<v-flex d-flex xs9 >
<v-layout class="mt-2 mb-0 pa-0" row wrap>
<x-flex d-flex xs12>
<div class="silver--text subheading">Yesterday's Revenue</div>
</x-flex>
<v-flex d-flex xs12>
<div class="silver--text display-1">{{totalUsersThis}}</div>
<v-btn outline class="darkgrey--text darken-1" right flat small>More</v-btn>
</v-flex>
</v-layout>
</v-flex>
</v-layout>
</v-container>
</v-card>
</v-flex>
<v-flex md3 sm6 xs12>
<v-card class="blue-grey darken-1">
<v-container fluid grid-list-sm light>
<v-layout class="mt-0 mb-0" row wrap>
<v-flex d-flex xs3>
<v-icon class="mx-0" x-large light>beenhere</v-icon>
</v-flex>
<v-flex d-flex xs9 >
<v-layout class="mt-2 mb-0 pa-0" row wrap>
<x-flex d-flex xs12>
<div class="silver--text subheading">This Week's Revenue</div>
</x-flex>
<v-flex d-flex xs12>
<div class="silver--text display-1">{{totalAmountThis}}</div>
<v-btn outline class="darkgrey--text darken-1" right flat small>More</v-btn>
</v-flex>
</v-layout>
</v-flex>
</v-layout>
</v-container>
</v-card>
</v-flex>
</v-layout>
<v-layout class="pt-1" row wrap>
<v-flex md4 xs12>
<v-card light>
<doughnut></doughnut>
</v-card>
</v-flex>
<v-flex md4 xs12>
<v-card light>
<bar></bar>
</v-card>
</v-flex>
<v-flex md4 xs12>
<v-card light class="Chart">
<line-chart></line-chart>
</v-card>
</v-flex>
</v-layout>
<v-layout>
<v-flex xs7>
<v-card>
<v-card-title>
Recent Transactions
<v-spacer/> <v-spacer/>
<v-text-field name="product" label="Search" light v-model="searchTransaction"></v-text-field>
</v-card-title>
<v-data-table v-bind:headers="headers" v-bind:items="items" v-bind:search="search"
v-bind:pagination.sync="pagination" hide-actions
class="elevation-1">
<template slot="items" slot-scope="props" class="body-2" >
<td class="body-2" >{{ props.item.account_from }}</td>
<td class="text-xs-left">{{ props.item.amount }}</td>
<td class="text-xs-left">{{ props.item.transaction_code}}</td>
<td class="text-xs-left">{{ props.item.payment_mode}}</td>
<td class="text-xs-left">{{ props.item.ref}}</td>
<td class="text-xs-left">{{ props.item.status}}
</td>
<td class="text-xs-left">{{ props.item.date}}</td>
<!-- <td class="text-xs-left">{{ props.item.quantity}}</td>
<td class="text-xs-left"><img :src='getImageURL(props.item.Product.phone_number)' height="90dp" width="90dp"/></td>
<td class="text-xs-left">
<v-btn fab small dark class="teal" #click.native="edit()">
<v-icon>edit</v-icon>
</v-btn>
<v-btn fab small class="cyan" #click.native="remove(props.item)">
<v-icon>delete</v-icon>
</v-btn>
</td>-->
</template>
</v-data-table>
<div class="text-xs-center pt-2">
<v-pagination v-model="pagination.page" :length="pages" circle></v-pagination>
</div>
</v-card>
</v-flex>
<v-flex xs5>
<v-card>
<v-card-title>
Merchants
<v-spacer/> <v-spacer/>
<v-text-field name="product" label="Search" light v-model="productSearch"></v-text-field>
</v-card-title>
<v-data-table v-bind:headers="headers2" v-bind:items="businesses" v-bind:search="search"
v-bind:pagination.sync="pagination2" hide-actions
class="elevation-1"
:loading="false">
<v-progress-linear slot="progress" color="blue" indeterminate></v-progress-linear>
<template slot="items" slot-scope="props" class="body-2" >
<td class="body-2" >{{ props.item.business_name }}</td>
<td class="text-xs-left">{{ props.item.short_code }}</td>
<td class="text-xs-left">{{ props.item.sender_id}}</td>
<!--<td class="text-xs-left">{{ props.item.payment_mode}}</td>
<td class="text-xs-left">KES {{ props.item.transaction_ref}}</td>
<td class="text-xs-left">KES {{ props.item.date}}</td>-->
<!-- <td class="text-xs-left">{{ props.item.quantity}}</td>
<td class="text-xs-left"><img :src='getImageURL(props.item.Product.phone_number)' height="90dp" width="90dp"/></td>
<td class="text-xs-left">
<v-btn fab small dark class="teal" #click.native="edit()">
<v-icon>edit</v-icon>
</v-btn>
<v-btn fab small class="cyan" #click.native="remove(props.item)">
<v-icon>delete</v-icon>
</v-btn>
</td>-->
</template>
</v-data-table>
<div class="text-xs-center pt-2">
<v-pagination v-model="pagination2.page" :length="pages2" circle></v-pagination>
</div>
</v-card>
</v-flex>
</v-layout>
</v-container>
</template>
<script>
import Bar from "../components/chart/Bar";
import Doughnut from "../components/chart/Doughnut";
import LineChart from "../components/chart/LineChart";
import { execute } from '../api'
export default {
name: "Dashboard",
components: {
Bar,
Doughnut,
LineChart
},
data () {
return {
searchTransaction:'',
items:[],
businesses:[],
totalUsers:'',
totalAmount:'',
totalUsersThis:'',
totalAmountThis:'',
search: '',
pagination: [],
pagination2: [],
productSearch:'',
headers: [
{
text: 'Phone Number',
left: true,
sortable: false,
value: 'phone_number'
},
{ text: 'Amount', value: 'amount' , sortable: false,},
{ text: 'Transaction Code', value: 'transaction_code' , sortable: false,},
{ text: 'Payment Mode', value: 'payment_mode' , sortable: false},
{ text: 'Transaction ref', value: 'transaction_ref' , sortable: false},
{ text: 'Status', value: 'status' , sortable: false},
{ text: 'date', value: 'date' , sortable: false}
], headers2: [
{
text: 'Business',
left: true,
sortable: false,
value: 'business'
},
{ text: 'ShortCode', value: 'short_code' , sortable: false,},
{ text: 'Sender ID', value: 'sender_id' , sortable: false,},
]
};
},
watch: {
searchTransaction: function() {
this.getTransactions();
}
},
methods: {
getCustomers () {
const data = new FormData()
data.append('TransactionType', 'getDashboardData')
execute(data).then((res) => {
this.totalUsers= new Intl.NumberFormat().format(res.data.data.IncomeRevenue)
this.totalAmount= new Intl.NumberFormat().format(res.data.data.TodayRevenue)
this.totalUsersThis= new Intl.NumberFormat().format(res.data.data.YesterdayRevenue)
this.totalAmountThis= new Intl.NumberFormat().format(res.data.data.ThisWeekRevenue)
}).catch((e) => {
// TODO
})
},
getTransactions () {
const data = new FormData()
data.append('TransactionType', 'getRecentTransactions')
data.append('keyword',this.searchTransaction)
execute(data).then((res) => {
this.items = res.data.data
}).catch((e) => {
// TODO
})
}, getBusinesses () {
const data = new FormData()
data.append('TransactionType', 'getBusinesses')
execute(data).then((res) => {
this.businesses = res.data.data
}).catch((e) => {
// TODO
})
}
},
computed: {
pages () {
return this.pagination && this.pagination.rowsPerPage ? Math.ceil(this.pagination.totalItems / this.pagination.rowsPerPage) : 0
},
pages2 () {
return this.pagination2 && this.pagination2.rowsPerPage ? Math.ceil(this.pagination2.totalItems / this.pagination2.rowsPerPage) : 0
}
},
mounted () {
this.getCustomers()
this.getTransactions()
this.getBusinesses()
}
};
</script>
<!--<style scoped>
#dashboard .flex {
margin-bottom: 2px;
}
</style>-->
Since you're using lots of <v-flex> elements in your code, my guess is you just made a typo and accidentally created a <x-flex> element. Just fix the typo (change all the x-flex to v-flex in your code) and it will work

Server side form validation with vue.js and vuetify

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
})