Vue Passing Data from component to another component - vue.js

I am making a "create account" flow for user and I am not able to pass data from one component to another. The first component has radio buttons with options of "tenant", "landlord", "contractor". Once the user selects "tenant", then the data should pass to the next step where they fill out a form with name and all that good stuff.. once they submit, it should all go together to the back end.
acc-for.vue with radio buttons component below.
<template>
<div>
<v-app
style="background-image: url('https://blog.modsy.com/wp-content/uploads/2019/06/D2_Full.jpg')"
>
<v-container class="pa-12">
<v-row>
<v-card class="pa-16">
<v-card-title>
Are you a?
</v-card-title>
<v-radio-group v-model="selectedValue" #change="selectedAcc">
<v-radio
v-for="account in accountType"
:key="account.name"
:value="account.name"
:label="account.name"
></v-radio>
</v-radio-group>
<v-row>
<v-btn rounded color="black" class="white--text" href="/login"
>Back</v-btn
>
<v-btn
rounded
color="black"
class="white--text"
#click="selected(accountSelected)"
>Next</v-btn
>
<!-- href="/create-acc" -->
</v-row>
</v-card>
</v-row>
</v-container>
</v-app>
</div>
</template>
<script>
import { mapMutations } from "vuex";
export default {
data() {
return {
selectedValue: false,
};
},
computed: {
accountType() {
return this.$store.state.accountType;
},
selected() {
return this.$store.state.selectedAccType;
},
},
methods: {
...mapMutations(["SELECTED_ACCOUNT_TYPE"]),
selectedAcc(e) {
this.$emit("selected-accountType", e);
},
},
};
</script>
<style></style>
createAccount.vue this component has the form for the fName and lName and all that good stuff..
<template>
<div>
<v-app
style="background-image: url('https://blog.modsy.com/wp-content/uploads/2019/06/D2_Full.jpg')"
>
<v-container class="pa-12">
<v-row>
<v-card class="pa-16">
<v-card-title>
{{ selectedTypeUser }}
</v-card-title>
<v-form>
<v-text-field
class="pa-4"
type="text"
v-model="newUser_fName"
label="First Name"
/>
<v-text-field
class="pa-4"
type="text"
v-model="newUser_lName"
label="Last Name"
/>
<v-text-field
class="pa-4"
type="text"
v-model="newUser_email"
label="Email"
/>
<v-text-field
class="pa-4"
type="text"
v-model="newUser_password"
label="Password"
/>
<v-row>
<v-btn rounded color="black" class="white--text" href="/acc-for"
>Back</v-btn
>
<v-btn
#click="registerUser"
rounded
color="black"
class="white--text"
>Next</v-btn
>
</v-row>
</v-form>
</v-card>
</v-row>
</v-container>
</v-app>
</div>
</template>
<script>
import { mapMutations } from "vuex";
import axios from "axios";
export default {
data() {
return {
newUser_fName: "",
newUser_lName: "",
newUser_email: "",
newUser_password: "",
};
},
methods: {
...mapMutations(["ADD_USER"]),
registerUser() {
let config = {
headers: {
"Content-Type": "application/json",
},
};
axios
.post(
"http://localhost:7876/createUser",
{
fName: this.newUser_fName,
lName: this.newUser_lName,
email: this.newUser_email,
password: this.newUser_email,
},
config
)
.then((response) => {
console.log(response.statusText);
})
.catch((e) => {
console.log("Error: ", e.response.data);
});
},
},
};
</script>
store.js (state) is below
// import vue from "vue";
import vuex from "vuex";
import axios from "axios";
import Vue from "vue";
Vue.use(vuex, axios);
export default new vuex.Store({
state: {
users: [],
accountType: [
{ name: "Tenant" },
{ name: "Landlord" },
{ name: "Contractor" }
],
selectedAccType: [],
},
mutations: {
ADD_USER: (state, payload) => {
state.users.push(payload.user);
},
SELECTED_ACCOUNT_TYPE: (state, payload) => {
state.selectedAccType.push(payload)
}
},
actions: {
addUser: ({ commit }, payload) => {
commit("ADD_USER", payload);
},
selectAcc: ({ commit }, payload) => {
commit("SELECTED_ACCOUNT_TYPE", payload)
}
},
// getters: {
// addAccountType(state, e) {
// state.accountType.push(e)
// },
// },
});

I see this on the button:
<v-btn rounded
color="black"
class="white--text"
#click="selected(accountSelected)"
>Next</v-btn
>
But I'm not finding a method with that name in your attached code. Double check that your names are all matched up and that you are calling the correct function on the click event.

Related

props date is null on submit with v-date-picker and Vue 2.6

Hello i'm trying to get the props date from children component Datepicker to the parent component Form but everytime when i submit i have date:null in console log.
I don't understand how to fecth it please ?
i'm using Vue 2.6 to use Vuetify with this datepicker
https://vuetifyjs.com/en/components/date-pickers/#formatting
here is the code :
<template>
<v-container>
<v-row>
<v-col cols="12" lg="6">
<v-menu v-model="menu" :close-on-content-click="false" transition="scale-transition" offset-y max-width="290px" min-width="auto">
<template v-slot:activator="{ on, attrs }">
<v-text-field v-model="computedDateFormatted" persistent-hint prepend-icon="mdi-calendar" readonly v-bind="attrs" v-on="on"></v-text-field>
</template>
<v-date-picker v-model="date" no-title #input="menu = false"></v-date-picker>
</v-menu>
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
name: "DatePicker",
// props: { date: { type: String } },
data: (vm) => ({
date: new Date(Date.now() - new Date().getTimezoneOffset() * 60000).toISOString().substr(0, 10),
dateFormatted: vm.formatDate(new Date(Date.now() - new Date().getTimezoneOffset() * 60000).toISOString().substr(0, 10)),
menu: false,
}),
computed: {
computedDateFormatted() {
return this.formatDate(this.date)
},
},
watch: {
date() {
this.dateFormatted = this.formatDate(this.date)
},
},
methods: {
formatDate(date) {
if (!date) return null
const [year, month, day] = date.split("-")
return `${month}/${day}/${year}`
},
parseDate(date) {
if (!date) return null
const [month, day, year] = date.split("/")
return `${year}-${month.padStart(2, "0")}-${day.padStart(2, "0")}`
},
},
}
</script>
<style lang="scss" scoped></style>
and the form component
<template>
<div>
<v-main>
<v-container class="white form-container">
<v-form
method="POST"
ref="form"
lazy-validation
#submit.prevent="postForm">
<!-- Datepicker -->
<v-row no-gutters>
<v-col sm="6" md="5" lg="5" class="bottom-row">
<v-label>Add a date</v-label>
<DatePicker :date="date" />
</v-col>
</v-row>
</v-form>
</v-container>
</v-main>
</div>
</template>
<script>
import axios from "axios"
import DatePicker from "./DatePicker.vue"
export default {
name: "Form",
components: {
DatePicker,
},
data() {
return {
date: null,
}
},
methods: {
async postForm(event) {
/**
* #param json
*/
const data = {
date: this.date,
}
const result = await axios.post("url", data)
console.log("Post date", data)
event.preventDefault()
},
},
}
</script>
<style lang="scss" scoped></style>

Vuejs Error: Property or method "cart" is not defined on the instance but referenced during render. Property"cartItems" is not defined

I have an error trying to get data from my vuex store.
Property or method "cart" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.
Property or method "cartItems" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property
pages/cart.vue
<template>
<div>
<v-container>
<section>
<h2 class="my-4">Your Cart</h2>
<v-divider class="my-4"></v-divider>
<h3>Total Amount: ${{ cart }}</h3>
<CartItem
v-for="item in cartItems"
:key="item.productId"
:prod-id="item.productId"
:name="item.name"
:image="item.image"
:price="item.price"
:qty="item.qty"
></CartItem>
</section>
</v-container>
</div>
</template>
<script>
import CartItem from '../components/CartItem'
import { mapGetters } from 'vuex'
export default {
components: {
CartItem,
computed: {
...mapGetters('cart',['totalSum'])
},
cartItems(){
return this.$store.getters['cart/products'];
}
}
}
</script>
<style scoped>
h2{
text-align: center;
}
h3{
text-align: center;
}
</style>
./components/CartItem.vue
<template>
<v-container class="my-5">
<v-row>
<v-col
sm="6"
md="4"
>
<v-card outlined>
<v-img :src="image" height="200px" />
<v-card-title> {{ name}} </v-card-title>
<v-card-subtitle> ${{ price }}</v-card-subtitle>
<v-card-subtitle> Quantity <strong>{{ qty }}</strong> </v-card-subtitle>
<v-card-actions>
<div>Total: ${{ itemTotal }}</div>
<v-btn #click="remove()" color="success" outlined >
<v-icon small left> add </v-icon>
Remove
</v-btn>
</v-card-actions>
</v-card>
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
props: ['prodId', 'name', 'image', 'price', 'qty'],
computed: {
itemTotal() {
return( this.price * this.qty).toFixed(2);
}
},
methods: {
remove(){
this.$store.dispatch('cart/removeFromCart', {productId: this.prodId})
}
}
}
</script>
store/modules/cart.js
export default {
namespaced: true,
state(){
return{
items: [],
total: 0,
qty: 0
}
},
mutations: {
addProductToCart(state, payload) {
const productData = payload;
const productInCartIndex = state.items.findIndex(
(ci) => ci.productId === productData.id
);
if (productInCartIndex >= 0) {
state.items[productInCartIndex].qty++;
} else {
const newItem = {
productId: productData.id,
title: productData.title,
image: productData.image,
price: productData.price,
qty: 1,
};
state.items.push(newItem);
}
state.qty++;
state.total += productData.price;
},
removeProductFromCart(state, payload) {
const prodId = payload.productId;
const productInCartIndex = state.items.findIndex(
(cartItem) => cartItem.productId === prodId
);
const prodData = state.items[productInCartIndex];
state.items.splice(productInCartIndex, 1);
state.qty -= prodData.qty;
state.total -= prodData.price * prodData.qty;
},
},
actions: {
addToCart(context, payload) {
const prodId = payload.id;
const products = context.rootGetters['prods/products'];
const product = products.find(prod => prod.id === prodId);
context.commit('addProductToCart', product);
},
removeFromCart(context, payload) {
context.commit('removeProductFromCart', payload);
}
},
getters: {
products(state) {
return state.items;
},
totalSum(state) {
return state.total;
},
quantity(state) {
return state.qty;
}
}
}
The property cartItems should be added to the computed option like :
export default {
components: {
CartItem,
},
computed: {
...mapGetters('cart',['totalSum']),
cartItems(){
return this.$store.getters['cart/products'];
}
}
}

Add/Edit list item at parent from child component form

I am developing a recipe app. At my CreateRecipe component, I have child component to add ingredients to the recipe or edit existing ingredients. Ill start by showing the code and what i want to achieve and then the problem
Parent component:
<template>
...
<v-dialog v-model="AddIgredientsDialog" max-width="800px">
<template v-slot:activator="{ on, attrs }">
<v-btn color="secondary" dark v-bind="attrs" v-on="on">
Add addIngredients
</v-btn>
</template>
<AddItemsForm
#addIngredient="SaveNewIgredient"
:newIngredientsItem="editedIgredient"
/>
</v-dialog>
</template>
<script>
import AddItemsForm from "./AddItemsForm"; //Child Component
data: () => ({
AddIgredientsDialog:false,
article: {
headline: "",
img: "",
content: "",
subHeader: "",
description: "",
igredients: [], //List to add/edit item at AddItemsForm child component
preperation: []
},
editedIgredient: { //Item to use for editing or adding new item to article.igredients
title: "",
subIgredients: []
},
defaultItem: { //Item used for resetting editedIgredient item
title: "",
subIgredients: []
},
editedIndex: -1, helper variable for knowing whether i need to add new item or edit exiting item
}),
methods:{
editIngredients(item) {
this.editedIndex = this.article.igredients.indexOf(item);
this.editedIgredient = Object.assign({}, item);
this.AddIgredientsDialog = true;
},
SaveNewIgredient(newItem) { //Triggered on #click of save button at child component New item is the
//item passed from children
if (this.editedIndex > -1) {
this.editedIgredient = Object.assign({}, newItem);
Object.assign(
this.article.igredients[this.editedIndex],
this.editedIgredient
);
} else {
this.article.igredients.push(this.editedIgredient);
}
this.AddIgredientsDialog = false;
this.$nextTick(() => {
this.editedIgredient = Object.assign({}, this.defaultItem);
this.editedIndex = -1;
});
},
}
</script>
Child Component:
<template>
<v-card>
<v-card-title>
<span class="headline">Add Ingredients</span>
</v-card-title>
<v-card-text>
<v-text-field v-model="newIngredientsItem.title" placeholder="כותרת">
</v-text-field>
<v-row align="center">
<v-col sm="11">
<v-text-field
v-model="newIgredient"
placeholder="New Igredient"
#keyup.enter="addNewIgredient"
>
</v-text-field>
</v-col>
<v-col sm="1">
<v-btn icon #click="addNewIgredient">
<v-icon>
mdi-plus
</v-icon>
</v-btn>
</v-col>
<v-col class="mt-0 pt-0" cols="12">
<v-row no-gutters>
<v-col cols="12">
<v-card flat tile>
<template
v-for="(item, index) in newIngredientsItem.subIgredients"
>
<v-list-item :key="index" class="mr-0 pr-0">
<v-list-item-content>
<v-list-item-title>
<v-edit-dialog #click.native.stop>
{{ item }}
<v-text-field
slot="input"
v-model="newIngredientsItem.subIgredients[index]"
></v-text-field>
</v-edit-dialog>
</v-list-item-title>
</v-list-item-content>
<v-list-item-action>
<v-btn icon #click="removeIgredient(index)">
<v-icon small>
mdi-delete
</v-icon>
</v-btn>
</v-list-item-action>
</v-list-item>
<v-divider
v-if="index + 1 < newIngredientsItem.subIgredients.length"
:key="item + index"
></v-divider>
</template>
</v-card>
</v-col>
</v-row>
</v-col>
</v-row>
</v-card-text>
<v-card-actions>
<v-btn color="primary" #click="AddIngredients">
Save
</v-btn>
</v-card-actions>
</v-card>
</template>
<script>
export default {
props: {
newIngredientsItem: {
type: Object,
default() {
return {
title: "",
subIgredients: [ ]
};
}
}
},
data: () => ({
newIgredient: ""
}),
methods: {
addNewIgredient() {
this.newIngredientsItem.subIgredients.push(this.newIgredient);
this.newIgredient = "";
},
AddIngredients() {
this.$emit("addIngredient", this.newIngredientsItem);
},
removeIgredient(index) {
this.newIngredientsItem.subIgredients.splice(index, 1);
}
}
};
</script>
My Problem:
At the moment im only trying to use the SaveNewIgredient() method.
After 1st time of adding item the item is added as it should and the parent defaultItem state remain as is which is good:
defaultItem: {
title: "",
subIgredients: []
},
After adding a second item the defaultItem changes and takes the editedItem properties.
For example if i add at the second time
{
title:'Test 1',
subIgredients: [
'Test 1 - 1',
'Test 1 - 2',
'Test 1 - 3',
]
}
That is what the defaultItem will be and then this assignment causes a bug
this.editedIgredient = Object.assign({}, this.defaultItem);
because editedItem should be:
{
title: "",
subIgredients: []
}
I tried to solve your problem. To do this I modified and in some places simplified your code to keep only what was close to the SaveNewIgredient() function. So here is my code.
Parent Component (for me App.vue):
<template>
<AddItemsForm #addIngredient="SaveNewIgredient" />
</template>
<script>
import AddItemsForm from "./AddItemsForm"; //Child Component
export default {
name: "App",
components: { AddItemsForm },
data() {
return {
article: {
igredients: [], //List to add/edit item at AddItemsForm child component
},
editedIgredient: {
//Item to use for editing or adding new item to article.igredients
title: "",
subIgredients: [],
},
defaultItem: {
//Item used for resetting editedIgredient item
title: "",
subIgredients: [],
},
};
},
methods: {
SaveNewIgredient(newItem) {
console.log("Received: ", newItem);
this.editedIgredient = newItem;
this.article.igredients.push({ ...this.editedIgredient });
console.log("defaultClear: ", this.defaultItem);
console.log("infoItem: ", this.editedIgredient);
this.editedIgredient = this.defaultItem;
console.log("defaultClear: ", this.defaultItem);
console.log("editedWillClear: ", this.editedIgredient);
console.log("listFinal: ", this.article.igredients);
},
},
};
</script>
Child Component (for me AddItemsForm.vue):
<template>
<div>
<input v-model="newIgredient" placeholder="New Igredient" />
<button #click="addNewIgredient">ADD</button>
<div>
<button color="primary" #click="AddIngredients">Save</button>
</div>
</div>
</template>
<script>
export default {
props: {
IngredientsItem: {
type: Object,
default() {
return {
title: "",
subIgredients: [],
};
},
},
},
data() {
return {
newIgredient: "",
title: "TEST",
titleNbr: 0,
resetIgredient: false,
};
},
computed: {
newIngredientsItem() {
return this.IngredientsItem;
},
},
methods: {
addNewIgredient() {
if (this.resetIgredient === true) {
this.newIngredientsItem.subIgredients = [];
}
this.newIngredientsItem.subIgredients.push(this.newIgredient);
this.newIgredient = "";
this.resetIgredient = false;
console.log("ADD: ", this.newIngredientsItem.subIgredients);
},
AddIngredients() {
this.newIngredientsItem.title = this.title + this.titleNbr;
this.titleNbr++;
console.log("EMIT: ", this.newIngredientsItem);
this.$emit("addIngredient", this.newIngredientsItem);
this.resetIgredient = true;
},
},
};
</script>

Vuejs - Same component with different data

I am trying to re-use the same component, but load the components with different data. I thought simply providing unique id's would do the trick, but no luck. I switched from a Vuex store for this data, to using a dataShare This is what I'm doing:
The components:
<logoHeader :panel=0 title="Add your logo / header" id="top" topPadding="pt-10" />
<logoHeader :panel=1 title="Add your main image" id="main" topPadding="pt-0"/>
So its the exact same component, with some different props and different ids
This is the logoHeader component:
<template>
<v-row
:class="topPadding"
align="center"
justify="center"
>
<v-col
align="center"
justify="center"
cols="12"
>
<v-hover v-slot:default="{ hover }">
<v-card
:elevation="hover ? 12 : 0"
class="mx-auto"
max-width="600"
>
<v-img
v-if="showImage"
:src="imageUrl"
max-width="600px"
class="pointer"
#click="panel = 0"
>
</v-img>
<v-icon
v-if="!showImage"
class="my_dark_purple_text"
size="100"
#click="sendToPanel"
>add_box</v-icon>
<p class="my_dark_purple_text">{{ title }}</p>
<p>URL {{ imageUrl }}</p>
<p>Show image? {{ showImage }}</p>
</v-card>
</v-hover>
</v-col>
</v-row>
</template>
<script>
import {mapGetters} from 'vuex';
import {mapActions} from 'vuex';
import {dataShare} from '../../../packs/fresh-credit.js';
export default {
props: ['panel', 'title', 'topPadding'],
data() {
return {
imageUrl: "",
showImage: false,
}
},
created() {
dataShare.$on('imageUrl', (data) => {
this.imageUrl = data;
});
dataShare.$on('showImage', (data) => {
this.showImage = data;
});
},
computed: {
...mapGetters('emailPanel', [
'returnPanel'
]),
},
methods: {
...mapActions('emailPanel', [
'updatePanel'
]),
sendToPanel() {
this.updatePanel(this.panel);
},
},
}
</script>
And then finally this is where the data enters the system:
<template>
<v-expansion-panel-content>
<h1 class="subtitle-1 font-weight-bold">Only images files accepted</h1>
<v-file-input
v-model="image"
accept="image/*"
label="Image Upload"
prepend-icon="add_a_photo"
color='#68007d'
></v-file-input>
<v-btn
:disabled="disableUpload"
color="#68007d"
class="white--text"
#click="sendImage"
>Submit</v-btn>
</v-expansion-panel-content>
</template>
<script>
import axios from 'axios';
import {dataShare} from '../../../../packs/fresh-credit.js';
export default {
data() {
return {
image: null,
disableUpload: true,
}
},
watch: {
image: function() {
if(this.image.size > 0){
this.disableUpload = false;
}
else{
this.disableUpload = true;
}
}
},
computed: {
},
methods: {
sendImage() {
let formData = new FormData();
formData.append('file', this.image);
axios.post('/admin/features/images', formData,
{
headers: {
'Content-Type': 'multipart/form-data'
}
}
).then(response => {
dataShare.$emit('imageUrl', response.data);
dataShare.$emit('showImage', true);
});
}
},
}
</script>
Where did I go astray?
Add the key property to the components and set it to different values (for example 1 and 2). If the key has different values Vue will differentiate them when rendering. Here is a basic explanation.

vue-i18n translation for validation error working only after form reset in vuetify form

I'm using form validation in application, I want my validation error message to be translated. It is getting translated only after reset form.
I have tried translation in computed property, and it's working when I do
computed as below, but error message is not disappearing after touched:
{
return [this.$t('LANGUAGE')]
}
In App Component
<template>
<div id="app">
<grid_Component/>
<div class="locale-changer" style="background: black">
<select v-model="$i18n.locale">
<option v-for="(lang, i) in lang" :key="`Lang${i}`" :value="lang">{{
lang }}
</option>
</select>
</div>
</div>
</template>
<script>
import form_Component from './components/form_Component.vue'
import form_Component from './components/form_Component.vue'
import i18n from './i18n'
export default {
data () {
return{
lang: ['GB', 'NO']
}
},
name: 'app',
components: {
form_Component,
},
}
</script>
GB.json file
{
"LANGUAGE": "English"
}
NO.json file
{
"LANGUAGE": "Norwegian"
}
form component
<template>
<v-form ref="form" v-model="valid" lazy-validation>
<v-text-field v-model="name" :counter="10" :rules="nameRules"
label="Name" required>
</v-text-field>
<v-btn :disabled="!valid" color="success" #click="validate">
Validate
</v-btn>
<v-btn color="error" #click="reset">
Reset Form
</v-btn>
<v-btn color="warning" #click="resetValidation">
Reset Validation
</v-btn>
</v-form>
</template>
<script>
export default {
data: () => ({
valid: true,
name: '',
}),
methods: {
validate () {
if (this.$refs.form.validate()) {
this.snackbar = true
}
},
reset () {
this.$refs.form.reset()
},
resetValidation () {
this.$refs.form.resetValidation()
}
},
computed: {
nameRules(){
return [v => !!v || this.$t('LANGUAGE')]
}
}
}
</script>