Missing required prop in Vue.js - vue.js

I am new to vue.js so maybe i'm missing something obvious. I have created 2 components Content.vue & ViewMore.vue. I am passing property "genre" which is inside array "animesByGenre" in Content.vue to ViewMore.vue but somehow it is not working.
Here is the Content.vue component:
<div v-for="(animesAndGenre, index) in animesByGenres" :key="index"
id="row1" class="container">
<h5>
{{animesAndGenre.genre.toUpperCase()}}
<button class="viewMore" v-bind:genre="animesAndGenre.genre"><router-link :to="{name: 'viewmore'}">view more</router-link></button>
</h5>
<vs-row vs-justify="center" class="row">
<vs-col v-for="(anime, i) in animesAndGenre.animes" :key="i"
vs-type="flex" vs-justify="center"
vs-align="center" vs-w="2" class="animeCard">
<vs-card actionable class="cardx">
<div slot="header" class="cardTitle">
<strong>
{{anime.attributes.canonicalTitle}}
</strong>
</div>
<div slot="media">
<img :src="anime.attributes.posterImage.medium">
</div>
<div>
<span>Rating: {{anime.attributes.averageRating}}</span>
</div>
<div slot="footer">
<vs-row vs-justify="center">
<vs-button #click="addAnimeToWatchlist(anime)"
color="primary" vs-type="gradient" >
Add to Watchlist
</vs-button>
</vs-row>
</div>
</vs-card>
</vs-col>
</vs-row>
</div>
<script>
import ViewMore from './ViewMore.vue';
import axios from 'axios'
export default {
name: 'Content',
components: {
'ViewMore': ViewMore,
},
data () {
return {
nextButton: false,
prevButton: false,
viewMoreButton: false,
results: '',
animes: '',
genres: ['adventure', 'action', 'thriller', 'mystery', 'horror'],
animesByGenres: []
}
},
created() {
this.getAnimes();
// this.getRowAnime();
this.genres.forEach( (genre) => {
this.getAnimeByGenres(genre);
});
},
}
</script>
Here is the ViewMore.vue component (i'm just trying to log genre for now):
<template>
<div>
</div>
</template>
<script>
import axios from 'axios'
export default {
props: {
genre: {
type: String,
required: true,
},
},
data() {
return {
allAnimes: '',
}
},
created() {
console.log(this.genre);
}
}
</script>

Passing props to routes doesn't work like that. Right now, all this code is doing is applying the genre prop to the button itself, not to the route it's going to. You'll need to add the genre to the URL as a param (/viewmore/:genre/), or as a part of the query (/viewmore?genre=...). See this page for how that works

Related

VUE Props still undefined how to fix it?

productPage
<template>
<div id="products" class="products">
<div class="container">
<h1 class="text-center p-5">熱銷商品</h1>
<div class="row">
<div v-for="product in products" class="col-md-3">
<div class="card product-item">
<swiper :pagination="true" :modules="modules" class="mySwiper">
<swiper-slide v-for="image in product.data().images">
<img :src="image" class="card-img-top" alt="..." />
</swiper-slide>
</swiper>
<div class="card-body">
<div
class="d-flex flex-column justify-content-between text-center"
>
<h5 class="card-title">{{ product.data().name }}</h5>
<h5 class="card-priceS">
{{ currency(product.data().price) }}$
</h5>
<p>{{ product.data().description }}</p>
</div>
<add-to-cart
:name="product.data().name"
:price="product.data().price"
:product-id="product.id"
:product-image="getImage(product.data().images)"
>
</add-to-cart>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Produtspage',
components: {
Navbar,
Login,
Swiper,
SwiperSlide,
},
props: {
msg: String,
},
setup() {
return {
modules: [Pagination],
}
},
data() {
return {
products: [],
}
},
mounted() {
this.readData()
},
methods: {
currency,
async readData() {
const querySnapshot = await getDocs(collection(db, 'products'))
querySnapshot.forEach((doc) => {
this.products.push(doc)
console.log(doc.id, ' => ', doc.data())
console.log(doc.data())
})
},
getImage(images) {
console.log(images)
return images
},
},
}
</script>
addtocart component
<template>
<div class="add-to-cart">
<button class="btn btn-success fs-6" #click="addToCart">
<i class="fa-solid fa-cart-plus mx-1"></i>加到購物車
</button>
</div>
</template>
<script>
export default {
name: 'AddToCart',
props: {
name: String,
price: String,
productId: String,
image: String,
},
data() {
return {
item: {
productName: this.name,
productPrice: this.price,
product_id: this.productId,
product_image: this.image,
// productQuantity: 1,
},
}
},
}
</script>
I checked the Vue Devtools, only image no props success, other data does have correct props, the image prop is still undefined, the image data is url and when I console.log(images), the data is shown, but props are still undefined.
I use firestore for the project, did I do anything wrong here?
you are using the wrong name for the binded attribute that dosnt match the prop name :
you are using :product-image in productPage
<add-to-cart
:name="product.data().name"
:price="product.data().price"
:product-id="product.id"
:product-image="getImage(product.data().images)"
>
so you have to match it with productImage instead of product-image as a prop name in the addtocart component
props: {
name: String,
price: String,
productId: String,
productImage: String, // appropriate prop name
},

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

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>

How to Add Vue Component as an HTML Tag

I'm new to using VueJS, and I'm working on a learning project right now.
I have a component called "Draggable", and another one called "ModalPage".
"ModalPage" is as follows:
The code for this page is below:
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<button #click="propagate">Propagate</button>
<h3>Vue Modals</h3>
<ul id="list">
</ul>
<!-- <div v-html="template"></div> -->
</div>
</template>
<script>
import draggable from '#/components/Draggable.vue';
export default {
name: 'ModelPage',
components: {
draggable,
},
data () {
return {
msg: 'Welcome to the Modal Popup Page',
template: `<draggable></draggale>`,
}
},
methods: {
propagate () {
// console.log("propagated")
list.append(`<draggable></draggale>`)
}
}
}
</script>
I also have a component called "Draggable" and its code is as follows:
<template>
<div id="app">
<VueDragResize :isActive="true" :w="200" :h="200" v-on:resizing="resize" v-on:dragging="resize">
<h3>Hello World!</h3>
<p>{{ top }} х {{ left }} </p>
<p>{{ width }} х {{ height }}</p>
</VueDragResize>
</div>
</template>
<script>
import VueDragResize from 'vue-drag-resize';
export default {
name: 'app',
components: {
VueDragResize
},
data() {
return {
width: 0,
height: 0,
top: 0,
left: 0
}
},
methods: {
resize(newRect) {
this.width = newRect.width;
this.height = newRect.height;
this.top = newRect.top;
this.left = newRect.left;
}
}
}
</script>
What i want to do is to be able to click the "Propagate" button on the page, and have a <draggable></draggable> html element appended to the page, as follows:
<ul id="list">
<draggable></draggable>
</ul>
I'm completely stumped with this. Can anyone help me please?
v-if will do your job
Updated
You can use v-for as discussion:
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<button #click="propagate">Propagate</button>
<h3>Vue Modals</h3>
<ul id="list">
<draggable v-for="index in count" :key="index>
</draggable>
</ul>
<!-- <div v-html="template"></div> -->
</div>
</template>
<script>
import draggable from '#/components/Draggable.vue';
export default {
name: 'ModelPage',
components: {
draggable,
},
data () {
return {
msg: 'Welcome to the Modal Popup Page',
template: `<draggable></draggale>`,
count: 0
}
},
methods: {
propagate () {
// console.log("propagated")
this.count++
}
}
}
</script>

how to hide sidebar in vue

guys, I m new to Vue and taken a coreui admin panel to develop some font vue but now I got stuck in this problem this is nav.js file
export default {
items: [
{
name: 'Product',
url: '/product',
icon: 'fa fa-cart-arrow-down',
children: [
{
name: 'Addproduct',
url: '/product/Addproduct',
},
{
name: 'Listproduct',
url: '/product/Listproduct',
}
]
},
]
}
main container
<template>
<div class="app">
<div class="app-body">
<Sidebar :navItems="nav"/>
<main class="main">
<div class="container-fluid">
<router-view></router-view>
</div>
</main>
<AppAside/>
</div>
</div>
</template>
<script>
import nav from '../_nav'
export default {
name: 'full',
components: {
Sidebar,
},
data () {
return {
nav: nav.items
}
},
computed: {
name () {
return this.$route.name
},
list () {
return this.$route.matched
}
}
}
</script>
here is my sidebar
<template v-for="(item, index) in navItems">
<template v-if="item.title">
<SidebarNavTitle :name="item.name" :classes="item.class" :wrapper="item.wrapper"/>
</template>
<template v-else>
<template v-if="item.children">
</template>
<template v-else>
<SidebarNavItem :classes="item.class">
<SidebarNavLink :name="item.name" :url="item.url" :icon="item.icon" :badge="item.badge" :variant="item.variant"/>
</SidebarNavItem>
</template>
</template>
</template>
i m stroing addproduct in my browser local storage now if when user login and go to dashboard then my i watch which url name is present in browser application or not if present show that else ignore now my problem is that how i can apply if condition like addproduct=addprodcut this this visible else hide
You could have a method in mounted hook, which can fetch data from localstorage and check if it's present in the url or not. Then assign it to a variable in main component which toggles the sidebar. Something like below should work:
<template>
<div class="app">
<div class="app-body">
<Sidebar :navItems="nav" v-if="showSidebar" />
<main class="main">
<div class="container-fluid">
<router-view></router-view>
</div>
</main>
<AppAside/>
</div>
</div>
</template>
<script>
import nav from '../_nav'
export default {
name: 'full',
components: {
Sidebar,
},
data () {
return {
nav: nav.items,
showSidebar: false
}
},
mounted () {
this.checkSidebarVisibility()
},
methods: {
checkSidebarVisibility: function() {
const inLocal = window.localStorage.getItems('your_item');
const inUrl = window.location.toString();
// check if inurl inside inLocal
if (inUrl is in inLocal) {
this.showSidebar = true;
} else {
this.showSidebar = false;
}
}
},
computed: {
name () {
return this.$route.name
},
list () {
return this.$route.matched
}
}
}
</script>