[Vue warn]: Error in v-on handler: "ReferenceError: cartItems is not defined" [closed] - vue.js

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 2 years ago.
Improve this question
so i rendering succusfully products , now i want to add the product id to the cart array however once i clicked on the add button it gives me this error
[Vue warn]: Error in v-on handler: "ReferenceError: cartItems is not defined"
hopefully u can give me a hint on how to fix the problem
thnak you so much
this is the code
<template>
<main>
<div class="margin"></div>
<div class="shopTitle">
<h1>Shop</h1>
</div>
<section>
<div v-if="show" class="products">
<div class="product" v-for="product in filteredProducts" :key="product.productId">
<div class="imgproduct"></div>
<div class="productDetails">
<div>
<h1>{{product.productTitle}}</h1>
</div>
<div>
<i class="fa fa-heart"></i>
</div>
<div>
<p>{{product.productPrice}}</p>
</div>
<div>
<i #click="addToCart(product)" class="fa fa-shopping-cart"></i>
</div>
</div>
</div>
</div>
</section>
</main>
</template>
<script>
export default {
data() {
return {
products: [
{
id: 1,
productTitle: "Shoes",
productImg: "../assets/ProductOne.png",
productPrice: "246$",
category: "Shoes"
},
{
id: 2,
productTitle: "Suits",
productImg: "../assets/ProductOne.png",
productPrice: "246$",
category: "Suits"
},
{
id: 3,
productTitle: "Bags",
productImg: "../assets/ProductOne.png",
productPrice: "246$",
category: "Bags"
}
],
selectedCategory: "All",
show: true,
cartItems: []
};
},
methods: {
addToCart(itemToAdd) {
// Add the item or increase qty
let itemInCart = this.cartItems.filter(item => item.id === itemToAdd.id);
let isItemInCart = itemInCart.length > 0;
if (isItemInCart === false) {
this.cartItems.push(itemToAdd);
} else {
}
console.log(cartItems)
}
}
};
</script>

console.log(cartItems)
should be
console.log(this.cartItems)

Related

Trying to access the store in a main page and I'm getting the error

Main.vue:
<template>
<div>
<div class="home_page_header">
<h1>My Recipe</h1>
<button #click="toggleOpen">Add New Recipe</button>
</div>
<div v-for="recipe in $store.state.recipes" :key="recipe.Recipe_Name">
<h1 >{{recipe.Recipe_Name}}</h1>
<p>{{recipe.Ingredients}}</p>
<router-link :to="`/recipe/${recipe.Recipe_Name}`">
<button>View Recipe</button>
</router-link>
<router-view/>
</div>
<div>
<h1></h1>
<p></p>
</div>
<div class="popUp" v-show="openUp">
<div>
<label for="receipe_name">Recipe Name</label>
<input type="text" id="receipe_name" v-model="values.receipeName"/>
</div>
<div>
<label for="ingredients_name">Ingredients</label>
<input type="text" id="ingredients_name" v-model="values.ingredientsName" v-for="i in values.ingredientsRows" :key="i"/>
<button #click="addrows" >Add Ingredients</button>
</div>
<div><button #click="onSubmit">Submit</button></div>
<div><button #click="toggleClose">Close</button></div>
</div>
</div>
</template>
<script>
import store from '#/store/index.js'
export default {
data() {
return {
values:{
receipeName:'',
ingredientsName:'',
ingredientsRows:1
},
values_final:{
receipeName:'',
ingredientsName:'',
ingredientsRows:1
},
openUp : false
}
},
methods: {
toggleOpen(){
this.openUp = true
},
toggleClose(){
this.openUp = false
},
onSubmit(){
if(this.values.receipeName || this.values.ingredientsName == ''){
alert('enter the full details')
}
},
addrows(){
this.values.ingredientsRows++;
},
},
computed: this.$store.commit('Add_Recipes',{...values_final})
}
</script>
store:
import { createStore } from 'vuex'
export default createStore({
state: {
recipes:[
{
Recipe_Name: 'curd',
Ingredients:'xxxxxx'
}
]
},
mutations: {
Add_Recipes (state,recipe) {
state.recipes.push(recipe)
}
},
Error : app.js:340 Uncaught TypeError: Cannot read properties of undefined (reading '$store')...
I'm trying to create a recipe app by using option API, I have one main page. In that main page that contains one title and add recipe button to add the details. And another one is a popup to enter the recipe details. so here after entering all the details that should show in a main page. I'm trying to access the store in a main page but iam getting the above error.
I guess you need to move the import statement of your store to your main.js file (not your Main.vue). And add app.use(store) after you created the app there with const app = createApp(…).

Removing specific object from array keeps removing last item

Here is what I have and I will explain it as much as I can:
I have a modal inside my HTML code as shown below:
<div id="favorites-modal-edit" class="modal">
<div class="modal-background"></div>
<div class="modal-card px-4">
<header class="modal-card-head">
<p class="modal-card-title">Favorites</p>
<button class="delete" aria-label="close"></button>
</header>
<section class="modal-card-body">
<div class="container">
<div id="favorites-modal-edit-wrapper" class="columns is-multiline buttons">
<favorites-edit-component v-for="(favorite, index) in favorites_list" :key="favorite.id" :favorite="favorite" />
</div>
</div>
</section>
<footer class="modal-card-foot">
<button class="button" #click="addItem">
Add Item
</button>
</footer>
</div>
</div>
The id="favorites-modal-edit" is the Vue.js app, then I have the <favorites-edit-component /> vue.js component.
Here is the JS code that I have:
I have my favorites_list generated which is an array of objects as shown below:
const favorites_list = [
{
id: 1,
name: 'Horse',
url: 'www.example.com',
},
{
id: 2,
name: 'Sheep',
url: 'www.example2.com',
},
{
id: 3,
name: 'Octopus',
url: 'www.example2.com',
},
{
id: 4,
name: 'Deer',
url: 'www.example2.com',
},
{
id: 5,
name: 'Hamster',
url: 'www.example2.com',
},
];
Then, I have my vue.js component, which is the favorites-edit-component that takes in the #click="removeItem(this.index) which is coming back as undefined on the index.
Vue.component('favorites-edit-component', {
template: `
<div class="column is-half">
<button class="button is-fullwidth is-danger is-outlined mb-0">
<span>{{ favorite.name }}</span>
<span class="icon is-small favorite-delete" #click="removeItem(this.index)">
<i class="fas fa-times"></i>
</span>
</button>
</div>
`,
props: {
favorite: Object
},
methods: {
removeItem: function(index) {
this.$parent.removeItem(index);
},
}
});
Then I have the vue.js app that is the parent as shown below:
new Vue({
el: '#favorites-modal-edit',
// Return the data in a function instead of a single object
data: function() {
return {
favorites_list
};
},
methods: {
addItem: function() {
console.log('Added item');
},
removeItem: function(index) {
console.log(index);
console.log(this.favorites_list);
this.favorites_list.splice(this.favorites_list.indexOf(index), 1);
},
},
});
The problem:
For some reason, each time I go to delete a item from the list, it's deleting the last item in the list and I don't know why it's doing it, check out what is happening:
This is the guide that I am following:
How to remove an item from an array in Vue.js
The item keeps coming back as undefined each time the remoteItem() function is triggered as shown below:
All help is appreciated!
There is an error in your favorites-edit-component template, actually in vue template, when you want to use prop, data, computed, mehods,..., dont't use this
=> there is an error here: #click="removeItem(this.index)"
=> in addition, where is index declared ? data ? prop ?
you're calling this.$parent.removeItem(index); then in removeItem you're doing this.favorites_list.splice(this.favorites_list.indexOf(index), 1); this means that you want to remove the value equal to index in you array no the value positioned at the index
=> this.favorites_list[index] != this.favorites_list[this.favorites_list.indexOf(index)]
In addition, I would suggest you to modify the favorites-edit-component component to use event so it can be more reusable:
favorites-edit-component:
<template>
<div class="column is-half">
<button class="button is-fullwidth is-danger is-outlined mb-0">
<span>{{ favorite.name }}</span>
<span class="icon is-small favorite-delete" #click="$emit('removeItem', favorite.id)">
<i class="fas fa-times"></i>
</span>
</button>
</div>
</template>
and in the parent component:
<template>
...
<div id="favorites-modal-edit-wrapper" class="columns is-multiline buttons">
<favorites-edit-component
v-for="favorite in favorites_list"
:key="favorite.id"
:favorite="favorite"
#removeItem="removeItem($event)"
/>
</div>
...
</template>
<script>
export default {
data: function () {
return {
favorites_list: [],
};
},
methods: {
...
removeItem(id) {
this.favorites_list = this.favorites_list.filter((favorite) => favorite.id !== id);
}
...
},
};
I would restructure your code a bit.
In your favorites-edit-component
change your removeItem method to be
removeItem() {
this.$emit('delete');
},
Then, where you are using your component (in the template of the parent)
Add an event catcher to catch the emitted "delete" event from the child.
<favorites-edit-component v-for="(favorite, index) in favorites_list" :key="favorite.id" :favorite="favorite" #delete="removeItem(index)"/>
The problem you have right now, is that you are trying to refer to "this.index" inside your child component, but the child component does not know what index it is being rendered as, unless you specifically pass it down to the child as a prop.
Also, if you pass the index down as a prop, you must refer to it as "index" and not "this.index" while in the template.

Issues with data bind in vue.js and events

I am working on a basic notepad app, for now the functionality is simple, create a note, when done click on the note from a list of previous created notes to view its details. I am not able to click on the note and see the details, instead I see the details of the component ShowNote.vue on the bottom of the notepad template, and in order to see the details I have to make the v-if="noteIsOpen to false". I am also not able to see the data from the data bind in ShowNote.vue file. Also when you click on the plus button the details from the note populate the page the button generates when clicked. I will paste screen shots of my code below. Please help me figure this out. I have tried to fix the props, and when I did I was able to see the note details finally.
App.vue
<template>
<div class="body">
<div class="notepad-container h-75 w-75">
<header class="header d-flex justify-content-center align-items-center">
<h4>Light Notepad v1</h4>
</header>
<section class="notepad-content" v-if="editorIsOpen === false">
<note-list
v-for="note in notes"
:key="note.id"
:note="note"
></note-list>
<add-note-button #open-editor="openNewEditor"></add-note-button>
</section>
<section class="notepad-editor" v-if="editorIsOpen === true">
<save-button></save-button>
</section>
<section class="notepad-content" v-if="noteIsOpen === true">
<show-note
:note="notes"
#open-note="readNote"
/>
</section>
<section class="notepad-content" v-if="noteIsOpen === false">
<show-note
:note="notes"
#open-note="openNote"
/>
</section>
</div>
</div>
</template>
<script>
import AddNoteButton from "./components/AddNoteButton.vue";
import NoteList from "./components/NoteList.vue";
import SaveButton from "./components/SaveButton.vue";
import ShowNote from "./components/ShowNote.vue";
export default {
components: {
NoteList,
AddNoteButton,
SaveButton,
ShowNote,
},
data() {
return {
editorIsOpen: false,
noteIsOpen: false,
notes: [
{
id: 1,
title: "1st Note",
body: "This is a note",
date: "10/17/20",
},
{
id: 2,
title: "2nd Note",
body: "This is a note",
date: "11/17/20",
},
],
};
},
methods: {
openNewEditor() {
this.editorIsOpen = !this.editorIsOpen;
},
readNote() {
this.noteIsOpen = !this.noteIsOpen;
},
},
};
</script>
AddNoteButton.vue
<template>
<div class="add-note-container" #click="openEditor">
<b-icon-plus-circle></b-icon-plus-circle>
</div>
</template>
<script>
import {BIconPlusCircle} from 'bootstrap-vue';
export default {
emits: ['open-editor'],
components: {
BIconPlusCircle
},
methods: {
openEditor() {
console.log('hello');
this.$emit('open-editor');
}
}
}
</script>
NoteList.vue
<template>
<div>
<b-list-group>
<b-list-group-item button #click="openNote()"
>{{ note.title }} - {{ note.date }}</b-list-group-item
>
</b-list-group>
</div>
</template>
<script>
export default {
emits: ['open-note'],
props: {
note: {
required: true,
},
},
methods: {
openNote() {
this.$emit('open-note');
console.log("clicked from NoteList");
},
},
};
</script>
ShowNote.vue
<template>
<div>
note details:
Note ID: {{ note.id }}, Date: {{ note.date }},
Title: {{ note.title }}, Body: {{ note.body }}
</div>
</template>
<script>
export default {
name: 'showNote',
props: {
note: {
required: true,
}
},
};
</script>
In your NoteList.vue you are emitting the event "open-note". But this event is never catched in your parent component named App.vue. You have to bind this event in order to get notified when ever you clicked on a note entry. Something like #open-note="openNote".
I figured out how to get the details to show when clicking on the note, for now I created a button in the notepad-content section:
<button class="readNoteButton" #click="readNote">view note one</button>
and changed the section with the show note component to:
<section v-if="readingNote === true" class="">
<show-note
#open-note="openNote"
v-for="note in notes"
:key="note.id"
:note="note"
></show-note>
</section>
issue I have now is figuring out how to get the details to show separately pertaining to each individual button

Getting error: Error: _ctx.openNote is not a function when I attempt to click a note to view

I stared from scratch with my notepad app, found here: Issues with data bind in vue.js and events
The issue was I can not seem to click on the note from NoteList.vue to see the details, there is a disconnect somewhere with my bind, and the event handler doesn't seem to work it is throwing an unhandled error: [Vue warn]: Unhandled error during execution of native event handler at <NoteList key=1 note={id: 1, title: "Note one title", body: "This is note ones body content"} > at
Here is my code:
App.vue
<template>
<div class="body">
<div class="notepad-container h-75 w-75">
<header class="header d-flex justify-content-center align-items-center">
<h4>Light Notepad v1</h4>
</header>
<section class="notepad-content">
<note-list
v-for="note in notes"
:key="note.id"
:note="note"
></note-list>
<!-- <add-note-button></add-note-button> -->
</section>
<section class="notepad-editor">
<!-- <save-button></save-button> -->
</section>
<section class="show-note"
v-if="readingNote" === true">
<show-note
#open-note="readNote"
v-for="note in notes"
:key="note.id"
:note="note"
></show-note>
</section>
</div>
</div>
</template>
<script>
// import AddNoteButton from "./components/AddNoteButton.vue";
import NoteList from "./components/NoteList.vue";
// import SaveButton from "./components/SaveButton.vue";
import ShowNote from "./components/ShowNote.vue";
export default {
components: {
NoteList,
// AddNoteButton,
// SaveButton,
ShowNote
},
data() {
return {
readingNote: false,
notes: [
{
id: 1,
title: "Note one title",
body: "This is note ones body content"
},
{
id: 2,
title: "Note two title",
body: "This is the second notes body content"
}
],
methods: {
readNote() {
this.readingNote = !this.readingNote;
}
}
};
},
};
</script>
NoteList.vue
<template>
<div>
<p #click="openNote">{{ note.title }}</p>
<hr />
</div>
</template>
<script>
export default {
emits: ["open-note"],
props: {
note: {}
},
method: {
openNote() {
this.$emit("open-note");
console.log("note opened!");
}
}
};
</script>
ShowNote.vue
<template>
<div>note details: {{ note.body }}</div>
</template>
<script>
export default {
props: {
note: {}
}
};
</script>
I figured it out, I had method, not methods, I was missing the 's' from methods, now I am able to see the consol.log message, still working on seeing the note detail. One step closer!
Maybe adding one more: I just encountered this when I added Vuex's ...mapActions into computed property in Vue component. ...mapActions belongs to methods.
WRONG:
computed: {
...mapGetters('AppStore', ['navigation', 'isNavigationCollapsed']),
...mapActions('AppStore', ['toggleNavigation'])
}
CORRECT:
computed: {
...mapGetters('AppStore', ['navigation', 'isNavigationCollapsed'])
},
methods: {
...mapActions('AppStore', ['toggleNavigation'])
}

Send data from one component to another in vue

Hi I'm trying to send data from one component to another but not sure how to approach it.
I've got one component that loops through an array of items and displays them. Then I have another component that contains a form/input and this should submit the data to the array in the other component.
I'm not sure on what I should be doing to send the date to the other component any help would be great.
Component to loop through items
<template>
<div class="container-flex">
<div class="entries">
<div class="entries__header">
<div class="entries__header__title">
<p>Name</p>
</div>
</div>
<div class="entries__content">
<ul class="entries__content__list">
<li v-for="entry in entries">
{{ entry.name }}
</li>
</ul>
</div>
<add-entry />
</div>
</div>
</template>
<script>
import addEntry from '#/components/add-entry.vue'
export default {
name: 'entry-list',
components: {
addEntry
},
data: function() {
return {
entries: [
{
name: 'Paul'
},
{
name: 'Barry'
},
{
name: 'Craig'
},
{
name: 'Zoe'
}
]
}
}
}
</script>
Component for adding / sending data
<template>
<div
class="entry-add"
v-bind:class="{ 'entry-add--open': addEntryIsOpen }">
<input
type="text"
name="addEntry"
#keyup.enter="addEntries"
v-model="newEntries">
</input>
<button #click="addEntries">Add Entries</button>
<div
class="entry-add__btn"
v-on:click="openAddEntry">
<span>+</span>
</div>
</div>
</template>
<script>
export default {
name: 'add-entry',
data: function() {
return {
addEntryIsOpen: false,
newEntries: ''
}
},
methods: {
addEntries: function() {
this.entries.push(this.newEntries);
this.newEntries = '';
},
openAddEntry() {
this.addEntryIsOpen = !this.addEntryIsOpen;
}
}
}
</script>
Sync the property between the 2:
<add-entry :entries.sync="entries"/>
Add it as a prop to the add-entry component:
props: ['entries']
Then do a shallow merge of the 2 and emit it back to the parent:
this.$emit('entries:update', [].concat(this.entries, this.newEntries))
(This was a comment but became to big :D)
Is there a way to pass in the key of name? The entry gets added but doesn't display because im looping and outputting {{ entry.name }}
That's happening probably because when you pass "complex objects" through parameters, the embed objects/collections are being seen as observable objects, even if you sync the properties, when the component is mounted, only loads first level data, in your case, the objects inside the array, this is performance friendly but sometimes a bit annoying, you have two options, the first one is to declare a computed property which returns the property passed from the parent controller, or secondly (dirty and ugly but works) is to JSON.stringify the collection passed and then JSON.parse to convert it back to an object without the observable properties.
Hope this helps you in any way.
Cheers.
So with help from #Ohgodwhy I managed to get it working. I'm not sure if it's the right way but it does seem to work without errors. Please add a better solution if there is one and I'll mark that as the answer.
I follow what Ohmygod said but the this.$emit('entries:update', [].concat(this.entries, this.newEntries)) didn't work. Well I never even need to add it.
This is my add-entry.vue component
<template>
<div
class="add-entry"
v-bind:class="{ 'add-entry--open': addEntryIsOpen }">
<input
class="add-entry__input"
type="text"
name="addEntry"
placeholder="Add Entry"
#keyup.enter="addEntries"
v-model="newEntries"
/>
<button
class="add-entry__btn"
#click="addEntries">Add</button>
</div>
</template>
<script>
export default {
name: 'add-entry',
props: ['entries'],
data: function() {
return {
addEntryIsOpen: false,
newEntries: ''
}
},
methods: {
addEntries: function() {
this.entries.push({name:this.newEntries});
this.newEntries = '';
}
}
}
</script>
And my list-entries.vue component
<template>
<div class="container-flex">
<div class="wrapper">
<div class="entries">
<div class="entries__header">
<div class="entries__header__title">
<p>Competition Entries</p>
</div>
<div class="entries__header__search">
<input
type="text"
name="Search"
class="input input--search"
placeholder="Search..."
v-model="search">
</div>
</div>
<div class="entries__content">
<ul class="entries__content__list">
<li v-for="entry in filteredEntries">
{{ entry.name }}
</li>
</ul>
</div>
<add-entry :entries.sync="entries"/>
</div>
</div>
</div>
</template>
<script>
import addEntry from '#/components/add-entry.vue'
import pickWinner from '#/components/pick-winner.vue'
export default {
name: 'entry-list',
components: {
addEntry,
pickWinner
},
data: function() {
return {
search: '',
entries: [
{
name: 'Geoff'
},
{
name: 'Stu'
},
{
name: 'Craig'
},
{
name: 'Mark'
},
{
name: 'Zoe'
}
]
}
},
computed: {
filteredEntries() {
if(this.search === '') return this.entries
return this.entries.filter(entry => {
return entry.name.toLowerCase().includes(this.search.toLowerCase())
})
}
}
}
</script>