Replacing the JSON from API with HTML in the Vue.js app - javascript

I have a simple Vite+Vue.js project in which I am importing data from headless-cms Wordpress using REST API and JSON. It should take and display titles and content of the posts (including imgs when they occure). I'm stuck because all the data on the page display like HTML, i.e. it contains HTML elements, but of course are JSON. Is there any way to convert it to plain HTML? I've tried filtering it with "replacer" method, but for all elements and situations it would take ages.
Screenshot of how data display on page
My component template:
<template>
<h1>Posts</h1>
<div v-for="post in posts" :key="post.id" class="posts">
<h2> {{ post.title.rendered }} </h2>
<div> {{ post.content.rendered }} </div>
</div>
My script in that component:
<script>
export default {
data() {
return {
posts: [],
message: String,
}
},
mounted() {
fetch('https://my-url-here.com/wp-json/wp/v2/posts')
.then(res => res.json())
.then(data => this.posts = data)
.then(err => console.log(err))
}
}
</script>

Just use v-html
<template>
<h1>Posts</h1>
<div v-for="post in posts" :key="post.id" class="posts">
<h2> {{ post.title.rendered }} </h2>
<div v-html="post.content.rendered"></div>
</div>

Related

I have a problem on The Movie DB API with VueJS

I work about a Movie App on The Movie DB API with VueJS but I have a problem; I'm trying to pull the poster_path data from The Movie App and print the picture on the page. The problem is; data is being get, the image is also visible, but when I refresh the page, the data cannot be retrieved. But when I remove or add ( / ) between w500 and ${movie.poster_path} in <img :src="`https://image.tmdb.org/t/p/w500/${movie.poster_path}`" > it's coming back. What should I do since we cannot do this all the time?
<script setup>
import axios from 'axios'
var movies = []
async function getMovies() {
const data = axios.get('https://api.themoviedb.org/3/movie/popular?api_key=**********&language=en-US&page=1')
const result = await data
result.data.results.forEach((movie) => {
movies.push(movie)
})
console.log(movies);
}
async function fetch() {
await this.getMovies()
}
getMovies()
</script>
<template>
<br><br><br><br>
<div class="">
<div class="">
<div class="" v-for="(movie, index) in movies" :key="index">
<div class="">
<img :src="`https://image.tmdb.org/t/p/w500/${movie.poster_path}`" >
</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
#import '../public/Home.scss';
</style>

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

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>

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);
});

str.split() is splitting at every letter and not at the delimiter

I'm trying to split a string of tags that I'm getting back from firebase by ',' and then store them in my data() for rendering. When I render out the firebase snapshot data after I split they tags in the console they are correctly formatted like so:
"tag1" "tag2"
but when I render them in my vue app, they are split at every letter (or at least a div is generated and is displaying for every letter) like so:
<div>"t"</div>
<div>"a"</div>
<div>"g"</div>
<div>"1"</div>
Can someone let me know if this is an issue with my vue app or my firebase call?
I have included some dummy data that I'm using to show the final result of how the tags array in data should look.
Here's my app for reference :)
<div id="container">
<div class="topbar">
<h3 id="header">DevDeep</h3>
<div id="searchDiv">
<b-form-input id="search" v-model="search" placeholder="search articles"></b-form-input>
<font-awesome-icon id="searchBtn" #click="searchResults()" icon="search" />
</div>
</div>
<!--main part of page-->
<div class="bod">
<div class="sideContainer">
<h4 id="category">categories</h4>
<ul id="listoflinks" v-for="cat in categories" :key="cat.toString()">
<div #click="searchResultsByCat(cat)">{{cat}}</div>
</ul>
</div>
<div id="centerContainer">
<div>
<h5> Tag list </h5>
<div class="flexContainer">
<div id="selectedTags" v-for="tag in tagList" :key="tag.toString()">
<span id="tag" #click="removeTag(tag)">{{tag}}</span>
</div>
<font-awesome-icon id="searchBtn" #click="searchbyTags()" icon="search" />
</div>
</div>
<div id="artDiv" v-for="art in articles" :key="art.title">
<div #click="gotoArticle(art)" id="thumbnail">
<h5 >{{art.title}}</h5>
<img :src=art.image height="100px" width="100px" alt="article thumbnail">
</div>
<!--TAGS-->/////////////////////////////////////////
<div class="flexContainer">
<div id="tags" v-for="tag in art.tags" :key="tag.toString()">
<span id="tag" #click="addTagToSearch(tag)">{{tag}}</span>
</div>
</div>
<!--TAGS-->//////////////////////////////////////////
</div>
</div>
<div class="addContainer">adds</div>
</div>
<!--main part of page-->
</div>
</template>
<script>
const fb = require('../../fireconfig.js')
export default {
name: 'Home',
data:function() {
return{
articles: [
{
title: 'modern web app security',
body: 'some content here about web app security',
image: 'dd',
tags: ['cyber security','web apps', 'web development']
},
{
title: 'intro to ArcGIS',
body: 'an article to show users how to do GIS stuff',
image: 'dwwd',
tags: ['arcgis','node js','gps services']
},
{
title: 'Vue.js injecting props',
body: 'this is how to inject vue props into a component',
image: 'dwwd',
tags: ['vue','props','components','web development','web apps']
}
],
categories:['web development', 'arcgis','cyber security','partnerships'],
search: '',
tagList: []
}
},
props: {
post: Object
},
created(){
console.log('db post will go here later')
let ref = fb.db.collection('articles')
ref.get()
.then(snapshot => {
if (snapshot.empty) {
console.log('No matching documents.');
return;
}
snapshot.forEach(doc => { //this works for each doc
console.log(doc.id, '=>', doc.data());
doc.data().tags = doc.data().tags.split(",") // its splitting each letter we need to only split at the comma
console.log(doc.data().tags)
this.articles.push(doc.data()) //push object into state array
})
})
.catch(err => {
console.log('Error getting documents', err);
});
},
}
</script>
doc.data() will produce a new object each time you call the method.
doc.data().tags = doc.data().tags.split(",") // its splitting each letter we need to only split at the comma
The above line is pointless you create a new object, but never store it in a variable. Which means that you can't use it later. Instead assign the resulting object to variable and use that.
const data = doc.data(); // create the data object only once
data.tags = data.tags.split(",");
this.articles.push(data);
Since the split of the tags didn't persist in the question code. tag in:
<div id="tags" v-for="tag in art.tags" :key="tag.toString()">
Is set to a character, because art.tags is a string and not an array.

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.

Categories