How to iterate on a collection (array) of objects in Nuxt? - javascript

I'm building a small project where it has a component in it, I should render the data from the API.
Here is my code:
<template>
<div>
<p v-if="$fetchState.pending">Fetching products...</p>
<p v-else-if="$fetchState.error">An error occurred :(</p>
<div v-else>
<h1>Nuxt products</h1>
<ul>
<li
v-for="(product, key) of product"
:key="product.id"
:img="product.img"
>
{{ product.description }}
</li>
</ul>
<button #click="$fetch">Refresh</button>
</div>
</div>
</template>
<script>
export default {
async fetch() {
this.products = await this.$axios("https://dummyjson.com/products");
},
};
</script>
and here is the error code:
Property or method "product" 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

This works
<template>
<div>
<p v-if="$fetchState.pending">Fetching products...</p>
<p v-else-if="$fetchState.error">An error occurred :(</p>
<div v-else>
<h1>Nuxt products</h1>
<ul>
<li v-for="product in products" :key="product.id" :img="product.img">
{{ product.description }}
</li>
</ul>
<button #click="$fetch">Refresh</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
products: [],
};
},
async fetch() {
const response = await this.$axios.$get('https://dummyjson.com/products')
this.products = response.products
},
}
</script>
You need v-for="product in products" as explained here: https://vuejs.org/guide/essentials/list.html
Also, regarding the the network request
We can see that as usual, the actual data is inside data, hence you can use the $get shortcut: https://axios.nuxtjs.org/usage#-shortcuts
Then you need to access the products field to have the data to iterate on. Using the Vue devtools + network tab greatly helps debugging that one!

so the answer is i missed putting the data as #kissu has mentioned above
<template>
<div>
<p v-if="$fetchState.pending">Fetching products...</p>
<p v-else-if="$fetchState.error">An error occurred :(</p>
<div v-else>
<h1>Nuxt products</h1>
<ul>
<li v-for="product in products" :key="product.id">
{{ product.description }}
{{ product.images }}
</li>
</ul>
<button #click="$fetch">Refresh</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
products: [],
};
},
async fetch() {
const response = await this.$axios.$get("https://dummyjson.com/products");
this.products = response.products;
},
};
</script>

Related

BlogCards Component doesn't show up on vue

I have a web app which shows the blog posts in a grid. But the BlogCards component in the Home.vue just outputs nothing, whereas it should output the blogs in a grid format. All the datas are stored in firebase. If I go to /blogs, I can see the blogs in grid format, but it doesn't work on the Home.vue. It also spits out the Vue Warn: property or method "blogPostsCards" is not defined on the instance but referenced during render.
I took this code from this tutorial at 5:31:05 minute mark.
Any solution to this problem.
Home.vue
<template>
<div class="home">
<BlogPost :post="post" v-for="(post, index) in blogPostsFeed" :key="index" />
<div class="blog-card-wrap">
<div class="container">
<h3>View more recent blogs</h3>
<div class="blog-cards">
<BlogCards :post="post" v-for="(post, index) in blogPostsCard" :key="index" />
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import BlogPost from '../components/BlogPost.vue'
import BlogCards from '../components/BlogCards.vue'
export default {
name: "Home",
components: {
BlogPost,
BlogCards,
Arrow
},
computed : {
blogPostsCards() {
return this.$store.getters.blogPostsCards;
},
blogPostsFeed() {
return this.$store.getters.blogPostsFeed;
},
}
};
</script>
BlogCards.vue
<template>
<div class="blog-card">
<img :src="post.blogCoverPhoto" alt="">
<div class="info">
<h4>{{ post.blogTitle }}</h4>
<h6>Posted on: {{ new Date(post.blogDate).toLocaleString('en-us', {dateStyle: "long"})}}</h6>
<router-link class="link" to="#" >
View Post <Arrow class="arrow" />
</router-link>
</div>
</div>
</template>
<script>
export default {
name: "blogCard",
props: ["post"],
computed: {
editPost() {
return this.$store.state.editPost
},
}
}
</script>
And getter function in store/index.js
getters:{
blogPostsFeed(state){
return state.blogPosts.slice(0,2);
},
blogPostsCards(state) {
return state.blogPosts.slice(2,6);
},
},
<BlogCards :post="post" v-for="(post, index) in blogPostsCard" :key="index" />
In your Home.vue >> change blogPostsCard to blogPostsCards because you use blogPostsCards in your computed so it gives you that error.

Vue: Retrieve firestore data to show user's business name v-for loop

In my project, I am working on the result page of the e-commerce.
I want to retrieve the data from two collections.
One is called "Product", and the other one is called "ProUser".
Now, I could retrieve and show the product info from "Product" collection.
I used v-for loop to show the each item's information.
I also want to retrieve shop name in "ProUser" collection of each user at the same time.
I set the shop name in "business" field.
Product collection is corresponding to uid.
In my dev console, I could confirm I could retrieve all the data from "ProUser" collection.
Following is my vue.js code.
Now in my browser, I cannot show the shop name of each user.
<template>
<div class="main">
<section class="cards">
<div class="card-list" v-for="(item, index) in getMenuItems" :key="index">
<div class="card" >
<div class="product-list" v-if="item.quantity > 0">
<div class="quantity">
<span>{{ item.quantity }}</span>
</div>
<div class="card__image-container" v-for="(sample, index) in item.sample" :key="index">
<router-link to="/product">
<img
:src="sample"
alt="Detailed image description would go here."
class="image"
/>
</router-link>
<!-- Retrieve business name from the ProUser collection. -->
<div class="card__content" v-for="(item, index) in ProUsers" :key="index">
<div class="card__info">
<span class="text--medium">{{ item.business }}</span>
<span class="card__distance text--medium"> product.quantity -- orders </span>
</div>
</div>
</div>
<div class="icons">
<div class="time">
<span>{{ item.limitObject }}</span>
</div>
<div class="fav">
<span>Heart</span>
</div>
<div class="price">
<span>{{ item.sale }}</span>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
import fireApp from '#/plugins/firebase'
const firebase = require("firebase");
require("firebase/firestore");
const db = firebase.firestore();
import googleMapMixin from '#/mixins/googleMapMixin'
import { mapGetters } from 'vuex'
export default {
name: "UserLocation",
data() {
return {
address: "",
error: "",
spinner: false,
ProUsers: []
}
},
mixins: [googleMapMixin],
created() {
//vuexfire
const dbMenuRef = db.collection('Product')
this.$store.dispatch('setMenuRef', dbMenuRef)
//firestore
db.collection("ProUser")
.get()
.then((querySnapshot)=> {
querySnapshot.forEach((doc) => {
console.log(doc.id, " => ", doc.data());
this.ProUsers.push(doc.data())
});
})
.catch((error) => {
console.log("Error getting documents: ", error);
});
},
computed: {
...mapGetters([
'getMenuItems'
])
},
methods: {
}
}
</script>
The problem now is, I could see all the shop name in <div class="card__info> tag.
What I want to achieve is to show one shop name of each user in here.
<!-- Retrieve business name from the ProUser collection. -->
<div class="card__content" v-for="(item, index) in ProUsers" :key="index">
<div class="card__info">
<span class="text--medium">{{ item.business }}</span>
<span class="card__distance text--medium"> product.quantity -- orders </span>
</div>
</div>
</div>
Not showing all the shop name.
My question is In other words, I want to fetch the product and the product owner.
Now in one tag, I could show one product with all the product owners in "ProUser" collection.
Hope someone helps me out.
If you need more information, please feel free to ask.
Here's how you can fetch data user from prouser collection and after then fetch the product related to the prouser and create a new object from data from both collection data.
let data = [];
let db = firebase.firestore();
db.collection("ProUser")
.get()
.then((userSnapshot) => {
db.collection("Product")
.get()
.then((productSnapshot) => {
userSnapshot.forEach((user) => {
productSnapshot.forEach((product) => {
if (user.id === product.id) {
data.push({
uid: user.id,
pId: product.id,
...user.data(),
...product.data(),
});
}
});
});
})
.catch((e) => {
console.log(e);
});
})
.catch((e) => {
console.log(e);
});

How can I get the selected data on my modal window(on button click) based on the v-for value?

I am new to Vue and am using the Bootstrap modals to display product information. I have grid containers that each have a product picture, description, and two buttons. One of the buttons(More details >>), when clicked, would shoot a modal window that should show the very same product description and picture of the grid it was contained in.
<div id="myapp">
<h1> {{ allRecords() }} </h1>
<div class="wrapper" >
<div class="grid-container" v-for="product in products" v-bind:key="product.ID">
<div class="Area-1">
<img class="product_image" src="https:....single_product.jpg">
</div>
<div class="Area-2">
<div class = "amount">
{{ product.amount }}
</div>
{{ product.Description }}
</div>
<div class="Area-3">
<b-button size="sm" v-b-modal="'myModal'" product_item = "'product'">
More Details >>
</b-button>
<b-modal id="myModal" >
<h1> {{ product.Name }} </h1>
<h3> {{ product.Description }} </h3>
</b-modal>
</div>
<div class="Area-4">
<br><button>Buy</button>
</div>
</div>
</div>
</div>
var app = new Vue({
'el': '#myapp',
data: {
products: "",
productID: 0
},
methods: {
allRecords: function(){
axios.get('ajaxfile.php')
.then(function (response) {
app.products = response.data;
})
.catch(function (error) {
console.log(error);
});
},
}
})
Area 1, 2 and 4 work perfectly fine and they display the product data according to the v-for value and as expected respectively for each grid container. Area 3 is a problem here when I click the More details >> button, I just see a faded black screen. I am not sure what I am doing wrong here, would really appreciate some help.
Add a property selectedProduct, then on More Details button click event, assign the current product to the selectedProduct member as below :
HTML
<div class="Area-3">
<b-button size="sm" v-b-modal="'myModal'"
#click="selectProduct(product)">More Details >> </b-button>
<b-modal id="myModal">
<h1> {{ this.selectedProduct.Name }} </h1>
<h3> {{ this.selectedProduct.Description }} </h3>
</b-modal>
</div>
Javascript:
var app = new Vue({
'el': '#myapp',
data: {
products: "",
productID: 0,
selectedProduct: {Name: '', Description: '', Amount:0}
},
methods: {
allRecords: function(){
...
},
selectProduct: function(product)
{
this.selectedProduct = product;
}
...
}
I can't replicate the issue. I created JSFiddle to test:
https://jsfiddle.net/4289wh0e/1/
However, I realized multiple modal elements are displayed when I click on the "More Details" button.
I suggest you add only one modal in the wrapper and store the chosen product in a data variable.
https://jsfiddle.net/4289wh0e/2/
<div id="myapp">
<h1> {{ allRecords() }} </h1>
<div class="wrapper">
<div class="grid-container" v-for="product in products" v-bind:key="product.ID">
<div class="Area-1"><img class="product_image" src="https:....single_product.jpg"> </div>
<div class="Area-2">
<div class="amount">{{ product.amount }} </div>
{{ product.Description }}</div>
<div class="Area-3">
<b-button size="sm" v-b-modal="'productModal'" #click="chooseProduct(product)" product_item="'product'">More Details >> </b-button>
</div>
<div class="Area-4">
<br>
<button>Buy</button>
</div>
</div>
<b-modal id="productModal" v-if="chosenProduct">
<h1> {{ chosenProduct.Name }} </h1>
<h3> {{ chosenProduct.Description }} </h3>
</b-modal>
</div>
</div>
Vue.use(BootstrapVue)
var app = new Vue({
'el': '#myapp',
data: {
products: [],
chosenProduct: null
},
methods: {
chooseProduct: function (product) {
this.chosenProduct = product
},
allRecords: function(){
this.products = [
{
ID: 1,
Description: 'dek',
Name: 'Name',
amount: 100
},
{
ID: 2,
Description: 'dek 2',
Name: 'Name 2',
amount: 300
}
]
},
}
})
The reason you're just seeing a black screen is because you're not giving the b-modal in your v-for a unique ID.
So when you click the button it's actually opening all the modals at the same time, and stacking the backdrop making it look very dark.
Instead you could use your product ID (I'm guessing it's unique) in your modal ID to make it unique
<div id="myapp">
<h1> {{ allRecords() }} </h1>
<div class="wrapper" >
<div class="grid-container" v-for="product in products" v-bind:key="product.ID">
<div class="Area-1">
<img class="product_image" src="https:....single_product.jpg">
</div>
<div class="Area-2"><div class = "amount">{{ product.amount }} </div>
{{ product.Description }}
</div>
<div class="Area-3">
<b-button size="sm" v-b-modal="`myModal-${product.ID}`" product_item = "'product'">
More Details >>
</b-button>
<b-modal :id="`myModal-${product.ID}`" >
<h1> {{ product.Name }} </h1>
<h3> {{ product.Description }} </h3>
</b-modal>
</div>
<div class="Area-4">
<br><button>Buy</button>
</div>
</div>
</div>
</div>
Example pen:
https://codepen.io/Hiws/pen/qBWJjOZ?editors=1010

push is not a function vuejs

I'm making an app with VueJS and Laravel. I'm getting an error, the push is not a function when I clicked an add to cart button. Everything is working here fine but methods addToCart gives error push is not a function. when I first click add to cart button it gives that error and once I refresh the page I can see a product in cart and again if click adds to cart button this time error is not seen, works perfectly. when cart[] is empty it gives error push is not a function, but when cart[] has at least one element I don't get that error.
Any help would be greatly appreciated.
productlist.vue
<template>
<div class="col-md-7">
<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">{{ product.name }}</h5>
<p class="card-text">{{ product.price }}
</p>
<button class="btn btn-primary" #click="addProductToCart(product)">Add to cart</button>
</div>
</div>
</div>
</template>
<script type="text/javascript">
export default{
props:['product'],
data(){
return{
}
},
methods:{
addProductToCart(product){
axios.post('/products/create',{
product : product
}).then((response)=>{
console.log(response)
this.$emit('addedToCart',product)
});
}
}
}
</script>
cart.vue
<template>
<div class="col-md-4">
<li v-for="(item,index) in cart">
{{ item.name }}-{{ item.price }}
<button #click="removeitem(index)">Remove</button>
</li>
</div>
</template>
<script type="text/javascript">
export default{
props:['cart'],
}
</script>
Main.vue
<template>
<div>
<div class="col-md-7" v-for="product in products">
<Productlist :product="product" #addedToCart="addedToCart"></Productlist>
</div>
<Cart :cart="cart" ></Cart>
</div>
</template>
<script type="text/javascript">
import Productlist from './Productlist';
import Cart from './Cart';
export default{
data(){
return{
products:[],
cart: [ ]
}
},
mounted() {
//get all products and show in page
axios.get('/products')
.then((response)=>{
this.products = response.data;
});
// get only those products that are added to cart
axios.get('/list')
.then((response)=>{
this.cart= response.data;
console.log(response)
});
},
methods:{
addedToCart(product){
this.cart.push(product)
}
},
components:{Productlist,Cart}
}
</script>
i don't sure if this will resolve your problem but is unnecessary do:
<button class="btn btn-primary" #click="addProductToCart(product)">Add to cart</button>
because you have product as prop of the component, should be #click="addProductToCart" without problem.
and your method should be so:
addProductToCart() {
axios
.post('/products/create', {
product: this.product,
})
.then(response => {
console.log(response);
this.$emit('addedToCart', this.product);
});
}
One thing more, use kebab-case to call the key string when you emit to the parent component:
this.$emit('addedToCart', this.product);
replace it with:
this.$emit('added-to-cart', this.product);
Then in your parent component you have:
<Productlist :product="product" #addedToCart="addedToCart"></Productlist>
replace it with:
<Productlist :product="product" #added-to-cart="addedToCart"></Productlist>
I guess this last things will resolve your problem according the Vue documentation.

How to run component's method via HTML in Vue2

I have a .vue file, ServiceList, which imports the component Information.vue. I would like to run Information's code in a loop in ServiceList's template like so:
ServiceList.vue
<template>
<div>
<h1>Headline</h1>
<div v-for="service in services">
<h2>{{ service.name }}</h2>
<information v-bind:service="service"/>
</div>
</div>
</template>
<script>
import Information from './Information'
... (more components here)
export default {
components: {
Information,
... (more components here)
},
... (rest of export here)
}
</script>
This is what Information looks like:
Information.vue
<template>
<div>
<p v-bind:id="'info'+service.id"/>
</div>
</template>
<script>
export default {
props:['service'],
data: function () {
return {
services: this.$store.state.Services.data
}
},
methods: {
getInfo: function (service) {
var info = '<b>Servicename:</b> <br>';
info += service.name;
... (method adds more to 'info' here)
document.getElementById('info'+service.id).innerHTML = info;
}
}
}
</script>
I have tried to do stuff like
<template>
<div>
<p v-bind:id="'info'+service.id"/>
{{ getInfo(service) }}
</div>
</template>
but it never seems to work. Weird thing is that when I make it a button,
<template>
<div>
<button v-on:click="getInfo(service)">GET INFO</button>
<p v-bind:id="'info'+service.id"/>
</div>
</template>
it works perfectly! But I don't want a button, I just want it to appear.
You don't need to manipulate DOM in Vue js for such trivial case, just add all you want to template and remove getInfo method, example:
Information.vue
<template>
<div>
<p>
<b>Servicename:</b> <br>
{{ service.name }}<br>
<b>Another Field:</b> <br>
{{ service.anotherField }}<br>
<b>Another Field 2 :</b> <br>
{{ service.anotherField2 }}<br>
</p>
</div>
</template>
OR if you really want to work with html do this:
Information.vue
<template>
<div>
<p v-html="getInfo(service)"/>
</div>
</template>
<script>
export default {
props:['service'],
data: function () {
return {
services: this.$store.state.Services.data
}
},
methods: {
getInfo: function (service) {
if (!service) return '';
var info = '<b>Servicename:</b> <br>';
info += service.name;
... (method adds more to 'info' here)
return info;
}
}
}

Categories