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.
Related
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>
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);
});
I'm trying to fetch product data from a JSON file, but can't get it to work.
I've tried several things and searched the internet for a solution but none of the examples on the internet equals my situation.
I'm new to both vue and axios, so please excuse my ignorance.
This is what I have so far:
Vue.component('products',{
data: {
results: []
},
mounted() {
axios.get("js/prods.json")
.then(response => {this.results = response.data.results})
},
template:`
<div id="products">
<div class="productsItemContainer" v-for="product in products">
<div class="productsItem">
<div class="">
<div class="mkcenter" style="position:relative">
<a class="item">
<img class="productImg" width="120px" height="120px" v-bind:src="'assets/products/' + product.image">
<div class="floating ui red label" v-if="product.new">NEW</div>
</a>
</div>
</div>
<div class="productItemName" >
<a>{{ product.name }}</a>
</div>
<div class="mkdivider mkcenter"></div>
<div class="productItemPrice" >
<a>€ {{ product.unit_price }}</a>
</div>
<div v-on:click="addToCart" class="mkcenter">
<div class="ui vertical animated basic button" tabindex="0">
<div class="hidden content">Koop</div>
<div class="visible content">
<i class="shop icon"></i>
</div>
</div>
</div>
</div>
</div>
</div>
`
})
new Vue({
el:"#app",
});
The json file is as follows
{
"products":[
{
"name": "Danser Skydancer",
"inventory": 5,
"unit_price": 45.99,
"image":"a.jpg",
"new":true
},
{
"name": "Avocado Zwem Ring",
"inventory": 10,
"unit_price": 123.75,
"image":"b.jpg",
"new":false
}
]
}
The problem is only with the fetching of the data from a JSON file, because the following worked:
Vue.component('products',{
data:function(){
return{
reactive:true,
products: [
{
name: "Danser Skydancer",
inventory: 5,
unit_price: 45.99,
image:"a.jpg",
new:true
},
{
name: "Avocado Zwem Ring",
inventory: 10,
unit_price: 123.75,
image:"b.jpg",
new:false
}
],
cart:0
}
},
template: etc.........
As the warnings suggest, please do the following:
Rename the data array from results to products since you are referencing it by the latter one as a name during render.
Make your data option a function returning an object since data option must be a function, so that each instance can maintain an independent copy of the returned data object. Have a look at the docs on this.
Vue.component('products', {
data() {
return {
products: []
}
},
mounted() {
axios
.get("js/prods.json")
.then(response => {
this.products = response.data.products;
});
},
template: `
//...
`
}
<div id="products">
<div class="productsItemContainer" v-for="product in products">
<div class="productsItem">
...
Also, since you're not using CDN (I think), I would suggest making the template a component with a separate Vue file rather than doing it inside template literals, something like that:
Products.vue
<template>
<div id="products">
<div class="productsItemContainer" v-for="product in products">
<div class="productsItem">
<!-- The rest of the elements -->
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Products',
data() {
return {
products: []
}
},
mounted() {
axios
.get("js/prods.json")
.then(response => {
this.products = response.data.products;
});
}
}
</script>
And then in your main JS file or anywhere else requiring this component:
import Products from './components/Products.vue';
new Vue({
el: '#app',
data() {
return {
//...
}
},
components: {
Products
}
})
<div id="app">
<Products />
</div>
How do I create very simple instant search filter based on input field value wherein the data is already displayed?
I created this fiddle and I would like the same functionality but I am just worrying if my back end developer will pull the data using PHP to display all the products and that said reactivity will not work.
So what is the best approach to make it work in this scenario?
Edit: So without the products data it will be like this .
Here is my HTML code:
<div id="app">
<div class="searchbox">
<input id="search-product" placeholder="Search Products" v-model="search" >
</div>
<div class="product-list">
<span class="product" v-for="product in filteredList">
<div class="product-img">
<img src="https://avatars2.githubusercontent.com/u/6325887?s=120&v=4">
<span class="price-tag">{{ product.price }}</span>
</div>
<div class="product-name" v-bind:data-sku="product.sku">{{ product.productName }}</div>
</span>
</div>
</div>
My script:
const app = new Vue({
el: '#app',
data: {
search: '',
products:[
{sku: "SS0001",productName: "alimango buttered",price: "seasonal", productCategory:"seafood specials"},
{sku: "SS0002",productName: "alimango steamed",price: "seasonal", productCategory:"seafood specials"},
{sku: "SS0003",productName: "chili crab with tomato sauce",price: "seasonal", productCategory:"seafood specials"},
{sku: "SS0004",productName: "malaga prito",price: "seasonal", productCategory:"seafood specials"},
]
},
computed: {
filteredList() {
return this.products.filter(product => {
return product.productName.toLowerCase().includes(this.search.toLowerCase())
})
}
}
});
I am learning to use vue js with wprest api by following watch-learn tutorials on the same topic. the problem is that the vue js version used in the tutorial seems to be v 1.x and i started using vue js 2.x. I was able to figure out the initial stages on how to display all the post using vue js 2.x.. I have an input field using which I search for a specific title it should filter and show the post. The issue is unable to workout exactly how it needs to be computed using vuejs 2.x.. I have included a codepen link containing the json data as well as my working code.
the following is the input field to be used to filter the posts by title
<div class="row">
<h4>Filter by name:</h4>
<input type="text" name="" v-model="nameFilter">
</div>
<div class="row">
<div class="col-md-4" v-for="post in posts">
<div class="card post">
<img class="card-img-top" v-bind:src="post.fi_300x180" >
<div class="card-body">
<h2 class="card-text">{{ post.title.rendered }}</h2>
<small class="tags" v-for="category in post.cats">{{ category.name }}</small>
</div>
</div> <!-- .post -->
</div> <!-- .col-md-4 -->
</div> <!-- .row -->
https://codepen.io/dhivyasolomon/pen/LdZKJY
I would appreciate any help in figuring out the next step. thanks.
You don't need directives, achieve this using the power of Computed properties
So you will have to itarate over the computed property which filter the posts by the input value and return a new array of posts.
Little example:
new Vue({
el: '#example',
computed: {
filteredPosts () {
return this.posts.filter(p => p.title.toLowerCase().includes(this.filterText.toLowerCase()))
}
},
data () {
return {
filterText: '',
posts: [
{
title: 'My first post title',
body: 'foo'
},
{
title: 'Another post title',
body: 'foo'
},
{
title: 'This will work fine',
body: 'foo'
},
{
title: 'Omg it\'s working!',
body: 'foo'
}
]
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.15/vue.js"></script>
<div id="example">
<input type="text" v-model="filterText" />
<ul>
<li v-for="post in filteredPosts">{{ post.title }}</li>
</ul>
</div>