Vue-Bootstrap navbar and vue-for - vue.js

I'm confused as to what I've missed in my code, when I try and use it, DevTools console says "Property or method "item" is not defined on the instance but referenced during render". Please could someone tell me what I've forgotten here, thanks!
<template>
<div>
<b-navbar toggleable="lg" type="dark" class='lx-navbar'>
<b-navbar-brand href="#">
<img src='../assets/images/navbar.png' alt="Logo" height='35px;'>
</b-navbar-brand>
<b-navbar-toggle target="nav-collapse"></b-navbar-toggle>
<b-collapse id="nav-collapse" is-nav>
<b-navbar-nav class="ml-auto">
<b-nav-item :v-for="item in items" :key="item.id" :to='item.to'>{{ item.name }}</b-nav-item>
</b-navbar-nav>
</b-collapse>
</b-navbar>
</div>
</template>
<script>
export default {
name: 'Header',
data() {
return {
items: [
{ id: 1, name: 'Home', to: '/' },
{ id: 2, name: 'GitHub', to: '/GitHub' },
{ id: 3, name: 'Socials', to: '/Socials' },
{ id: 4, name: 'Nova', to: '/Nova' }
]
}
}
}
</script>
<style>
.lx-navbar {
background: rgb(35, 45, 61);
}
</style>

You shouldn't binding v-for
:v-for is wrong

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

How to read child component values from a parent method -- vue.js

I have a dynamic list of custom components which are input fields. From my parent vue ,when a button is clicked, how can I write a method that will loop through all the components in my list to get their values?
input-field.vue
<template>
<div>
<input
type="text"
:name="name"
:id="id"
#input="valueChanged"
v-model="val"
/>
</div>
</template>
app.vue
<div>
<ul>
<ul v-for="item in items" :key="item.id">
<my-input :name="item.name" :id="item.id" #input="onChange" />
</ul>
</ul>
<button>READ FIELDS</button>
</div>
</template>
<script>
import myInput from "./components/input-field.vue";
export default {
name: "App",
data() {
return {
items: [
{ id: "3", name: "test3" },
{ id: "4", name: "test4" },
{ id: "5", name: "test5" },
],
};
},
components: {
myInput
},
methods: {
//on button click function here
//how can I get the data from the my-input components
}
};
</script>
There is no need to read the values. Bind them like this: Playground
Read and understand what I have done, do not copy-paste. I have changed some of your code eg. imports
input-field.vue
<template>
{{name}}
<input type="text"
:name="name"
:id="id"
:value="modelValue"
#input="$emit('update:modelValue', $event.target.value)" />
</template>
<script>
export default {
props: ["modelValue", "id", "name"],
emits: ["update:modelValue"]
}
</script>
app.vue
<template>
<div v-for="item in items" :key="item.id">
<my-input :name="item.name" :id="item.id" v-model="item.value"></my-input>
</div>
<br />
<b>Values in App.vue</b>
<table border=1>
<tr v-for="item in items" :key="item.id">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.value}}</td>
</tr>
</table>
</template>
<script>
import myInput from "./input-field.vue";
export default {
name: "App",
data() {
return {
items: [
{ id: "3", name: "test3", value: "test input" },
{ id: "4", name: "test4", value: "" },
{ id: "5", name: "test5", value: "" },
],
};
},
components: {
myInput
},
};
</script>

Vue.js v3 passing data through router link

I have an object in Portfolio.vue like this:
data() {
return {
portfolio: {
front: [
{ description: '1. project ', src: require("../assets/sample.jpg"), slug: 'first'},
{ description: '2. project', src: require("../assets/sample.jpg"), slug: 'second' },
{ description: '3. project', src: require("../assets/sample.jpg"), slug: 'third' },
]
}
}
}
Portfolio.vue:
<div class="">
<div v-for="(data,index) in portfolio.front" :key="index">
<router-link :to="'/portfolio/'+data.slug"> <div class="element" :data-description="data.description">
<img :src="data.src " alt="">
</div>
</router-link>
</div>
</div>
PortfolioProduct.vue
<template>
<div>
<p>I want to take data to here. In here, i have to reach the data like this: portfolio.front.description</p>
</div>
</template>
<script>
export default {
props: {
},
}
</script>
My routes:
const routes = [
{
path: "/portfolio",
name: "Portfolio",
component: Portfolio,
},
{
path: "/portfolio/:id",
name: "PortfolioProduct",
component: PortfolioProduct,
props: true,
},
];
I want to take data from Portfolio.vue to PortfolioProduct.vue , i couldn't solve. I'm using vue js3 , if you help me i will be glad. Thank you
You could probably use the template literal, changing the router-link would be like so:
<router-link :to="`/portfolio/${data.slug}`"> <div class="element" :data-description="data.description">

Property or method "posts" is not defined on the instance but referenced during render

I am trying to replicate the vue tutorial example (found here: https://v3.vuejs.org/guide/component-basics.html#passing-data-to-child-components-with-props ), but with no success. Below is my code. I have a view called TA.vue, that i would like to import the component into and render. Any ideas what I am doing wrong?
TA.vue (the view):
<template id="front">
<b-container style="margin-top: 9rem;">
<b-row>
<div id="blog-posts-events-demo" class="demo">
<div>
<blog_post
v-for="post in posts"
:key="post.id"
:title="post.title"
></blog_post>
</div>
</div>
</b-row>
</b-container>
</template>
<script>
import blog_post from '../components/blog_post' // import the Header component
export default {
name: 'talent-acquisition',
components: {
blog_post
}
}
</script>
blog_post component:
Vue.component('blog_post', {
el: '#front',
data() {
return {
posts: [
{ id: 1, title: 'My journey with Vue'},
{ id: 2, title: 'Blogging with Vue'},
{ id: 3, title: 'Why Vue is so fun'}
]
}
},
props: ['title'],
template: `
<div class="blog_post">
<h4>{{ title }}</h4>
</div>
`
})
app.mount('#blog-posts-events-demo')
EDIT:
After following Amaarrockz suggestion, I have the following error:
vue.runtime.esm.js?2b0e:619 [Vue warn]: Failed to mount component: template or render function not defined.
found in
---> <BlogPost> at src/components/blog_post.vue
<TalentAcquisition>
<App> at src/App.vue
<Root>
The thing is you have array 'posts' defined in your child component('blog_post') which you are trying to iterate from the parent.
The better implementation would be to define the array in the parent component .
Please find the modifications below
TA.vue
<template id="front">
<b-container style="margin-top: 9rem;">
<b-row>
<div id="blog-posts-events-demo" class="demo">
<div>
<blog_post
v-for="post in posts"
:key="post.id"
:title="post.title"
></blog_post>
</div>
</div>
</b-row>
</b-container>
</template>
<script>
import blog_post from '../components/blog_post' // import the Header component
export default {
name: 'talent-acquisition',
components: {
blog_post
},
data() {
return {
posts: [
{ id: 1, title: 'My journey with Vue'},
{ id: 2, title: 'Blogging with Vue'},
{ id: 3, title: 'Why Vue is so fun'}
]
}
},
}
</script>
blog_post component:
Vue.component('blog_post', {
el: '#front',
props: ['title'],
template: `
<div class="blog_post">
<h4>{{ title }}</h4>
</div>
`
})
app.mount('#blog-posts-events-demo')

Change component state form another component rendered in a v-for

I have a list of components rendered in a a v-for. I want to set the "show" Boolean property as false in the other components when one of them is set to true:
To simplify I am only adding two components
Main component code:
<template>
<aside class="main-sidebar">
<section class="sidebar">
<ul class="sidebar-menu" data-widget="tree">
<nav-bar-user-profile-item></nav-bar-user-profile-item>
<nav-bar-item></nav-bar-item>
<nav-bar-item></nav-bar-item>
</ul>
</section>
</aside>
</template>
<script>
import NavBarUserProfileItem from '#/components/NavBar/NavBarUserProfileItem';
import NavBarItem from '#/components/NavBar/NavBarItem';
export default {
name: 'NavBar',
components: {
NavBarUserProfileItem,
NavBarItem
},
methods: {
MenuHasBeenToggled(event) {
console.log(event);
}
}
}
NavBarItemComponent
<template>
<li class="treeview2 item" :class="{'menu-open': isOpen, 'active': menu.active}" #click="ToggleState">
<a href="#">
<i class="fa fa-th"></i>
<span>{{ menu.title }}</span>
<span class="pull-right-container">
<i class="fa fa-angle-right pull-right"></i>
</span>
</a>
<collapse-transition>
<ul class="treeview-menu" v-show="isOpen">
<li v-for="submenu in menu.submenus" :key="submenu.title" :class="{'active': (('active' in submenu) ? submenu.active : false)}">
<b-link :href="submenu.link">
<i class="fa fa-circle-thin"></i>
{{ submenu.title }}
</b-link>
</li>
</ul>
</collapse-transition>
</li>
</template>
<script>
export default {
name: 'NavBarItem',
data: function () {
return {
isOpen: false
}
},
computed: {
},
methods: {
ToggleState() {
this.isOpen = !this.isOpen;
this.$emit("toggle-state");
}
},
props: {
menu: {
type: Object,
default: function() {
return {
link: "#",
title: "Main menu",
active: true,
submenus: [
{
link: "#",
title: "Submenu 1",
},
{
link: "#",
title: "Submenu 2",
active: true
},
{
link: "#",
title: "Submenu 3",
},
]
}
}
}
}
}
</script>
<style scoped>
</style>
The goal is to click on one of the and show the menu contents while at the same time collapse the other components.
I thought about using an array of variables and bind it to the "show" prop and with an event listen to it and set every variable to false except the one form the component that sent the event.
How can I know which component sent the event?
Any better idea on how to accomplish this task?
I think, the best way to do it is to add a uniuque identifier property to each NavBarItem and a property for a selected NavBarItem. Then in the main component you can on click on NavBarItem set selected NavBarItem and in NavBarItem make the isOpen computed on the basis if current NavBarItem identifier equals the clicked NavBarItem. Something like this:
<template>
<aside class="main-sidebar">
<section class="sidebar">
<ul class="sidebar-menu" data-widget="tree">
<nav-bar-user-profile-item></nav-bar-user-profile-item>
<nav-bar-item item-id="1" :selected-item-id="selectedNavbarItemId" #click="selectedNavBarItemId = 1"></nav-bar-item>
<nav-bar-item item-id="2" :selected-item-id="selectedNavbarItemId" #click="selectedNavBarItemId = 2"></nav-bar-item>
</ul>
</section>
</aside>
</template>
<script>
import NavBarUserProfileItem from '#/components/NavBar/NavBarUserProfileItem';
import NavBarItem from '#/components/NavBar/NavBarItem';
export default {
name: 'NavBar',
components: {
NavBarUserProfileItem,
NavBarItem
},
data: function(){
return {
selectedNavBarItemId: 0
}
},
methods: {
MenuHasBeenToggled(event) {
console.log(event);
}
}
}
And in NavBarItem
<template>
<li class="treeview2 item" :class="{'menu-open': isOpen, 'active': menu.active}" #click="ToggleState">
<a href="#">
<i class="fa fa-th"></i>
<span>{{ menu.title }}</span>
<span class="pull-right-container">
<i class="fa fa-angle-right pull-right"></i>
</span>
</a>
<collapse-transition>
<ul class="treeview-menu" v-show="isOpen">
<li v-for="submenu in menu.submenus" :key="submenu.title" :class="{'active': (('active' in submenu) ? submenu.active : false)}">
<b-link :href="submenu.link">
<i class="fa fa-circle-thin"></i>
{{ submenu.title }}
</b-link>
</li>
</ul>
</collapse-transition>
</li>
</template>
<script>
export default {
name: 'NavBarItem',
data: function () {
return {
}
},
computed: {
isOpen:function(){
return itemId == selectedItemId;
}
},
methods: {
},
props: {
itemId:Number,
selectedItemId:Number,
menu: {
type: Object,
default: function() {
return {
link: "#",
title: "Main menu",
active: true,
submenus: [
{
link: "#",
title: "Submenu 1",
},
{
link: "#",
title: "Submenu 2",
active: true
},
{
link: "#",
title: "Submenu 3",
},
]
}
}
}
}
}
</script>
<style scoped>
</style>