Having trouble using database data in vue app? - javascript

Hi I've been trying to get multiple profiles from a database and use v-for to try and display them all but whenever I try it doesn't work on and I can't figure out how to get it to use the data which should be getting imported.
Below is what the javascript file looks like.
import { projectFirestore } from "../Firebase/Config";
import { ref } from "vue"
const getPremium = () => {
const Premium = ref([])
const error = ref(null)
const load = async () => {
try{
const res = await projectFirestore.collection('Premium').get()
Premium.value = res.docs.map(doc => {
console.log(doc.data())
return {...doc.data(), id: doc.id}
})
}
catch (err){
error.value = err.message
console.log(error.value)
}
}
return { Premium, error, load}
}
export default getPremium
And heres the vue where I am trying to use v-for to create the profiles.
<script>
import getPremium from "../Composables/getPremium.js";
const {Premium, error, load} = getPremium();
load();
</script>
<template>
<div v-for =" Premium in getPremiums" :key="Premium.id" >
<div class= "hover:scale-105 transition ease-in-out duration-300 bg-neutral-800 hover:bg-neutral-900 active:bg-neutral-900 text-neutral-400 font-bold rounded-xl">
<br>
<p>{{ Premium.Name }}</p>
<img src="../assets/Sample-pic.png" class="object-contain ml-6 w-60 h-80 transition ease-in-out duration-300">
<div class="grid grid-cols-2 grid-rows-fit text-left ml-6">
<p>Location:</p>
<p>{{ Premium.Location }}</p>
<p>Rates:</p>
<p>{{ Premium.Rates }} /hr</p>
<p>Phone:</p>
<p>{{ Premium.Phone }}</p>
</div><br>
</div>
</div>
</template>
I'm not sure what I need to do from here to get it to work properly, I'm new to using databases and any help would be greatly appreciated, Thankyou

Besides, you are trying to iterate through function, you iterate it using in operator. In operator iterates object properties, no arrays. And created variables call using first small character not big one like you have in code "Premium".
<script>
import getPremium from "../Composables/getPremium.js";
const { premiumList, error, load } = getPremiumList(); // Name variables, functions with first small letter not big one. Interfaces, Clases with first big letter. Add "List" or "s" in end of name so everyone knows it is array.
load();
</script>
<template>
<main>
<div v-for="item of premiumList" :key="item.id">
<div
class="hover:scale-105 transition ease-in-out duration-300 bg-neutral-800 hover:bg-neutral-900 active:bg-neutral-900 text-neutral-400 font-bold rounded-xl"
>
<br />
<p>{{ item.Name }}</p>
<img
src="../assets/Sample-pic.png"
class="object-contain ml-6 w-60 h-80 transition ease-in-out duration-300"
/>
<div class="grid grid-cols-2 grid-rows-fit text-left ml-6">
<p>Location:</p>
<p>{{ item.Location }}</p>
<p>Rates:</p>
<p>{{ item.Rates }} /hr</p>
<p>Phone:</p>
<p>{{ item.Phone }}</p>
</div>
<br />
</div>
</div>
</main>
</template>

You are destructuring Premium from getPremium(), so that is what you should use in your v-for
<script>
import getPremium from "../Composables/getPremium.js";
const { Premium, error, load } = getPremium();
load();
</script>
<template>
<main>
<div v-for="item in Premium" :key="item.id">
<div
class="hover:scale-105 transition ease-in-out duration-300 bg-neutral-800 hover:bg-neutral-900 active:bg-neutral-900 text-neutral-400 font-bold rounded-xl"
>
<br />
<p>{{ item.Name }}</p>
<img
src="../assets/Sample-pic.png"
class="object-contain ml-6 w-60 h-80 transition ease-in-out duration-300"
/>
<div class="grid grid-cols-2 grid-rows-fit text-left ml-6">
<p>Location:</p>
<p>{{ item.Location }}</p>
<p>Rates:</p>
<p>{{ item.Rates }} /hr</p>
<p>Phone:</p>
<p>{{ item.Phone }}</p>
</div>
<br />
</div>
</div>
</main>
</template>

Related

orderBy on a Nuxt3 v-for loop

I have a Nuxt3 project and my v-for loop is working, but I can't figure out how to order my list.
I know it's a Vue thing and not Nuxt, but I am new to both.
<template>
<div class="max-w-7xl mx-auto my-10 sm:px-6 lg:px-8">
<div v-if="pending">Loading ...</div>
<div v-else>
<ul class="list-none" style="columns: 4">
<li
v-for="nda in ndas"
v-bind:key="nda.id"
class="font-color: text-slate-600 hover:text-red-600 hover:underline"
>
<Nuxt-link :to="'/ndas/' + nda.id">
{{ nda.user_signature }}
</Nuxt-link>
</li>
</ul>
</div>
</div>
</template>
<script setup>
const { pending, data: ndas } = useLazyFetch(
"https://***/nda-data.json"
);
watch(ndas, (newNdas) => {});
</script>
You need to computed filtered array for this. Just create a new computed property and return filtered array.
const filteredNdas = computed(() => {
return ndas.filter(item => item.id > 5)
})
And so use your new array in the v-for loop
v-for="nda in filteredNdas"

Method "getChampionName" has type "undefined" in the component definition. Did you reference the function correctly?

I'm trying to use this function (src/api/)
function getChampionName(champId) {
axios.get('http://ddragon.leagueoflegends.com/cdn/12.5.1/data/en_US/champion.json')
.then(({ data }) => {
let list = data
let championList = list.data
for (var i in championList) {
if (championList[i].key == champId) {
return championList[i].id
}
}
})
.catch((err) => console.log(err))
}
export {getChampionName}
In this component (src/components/)
<template>
<div class="w-72">
<header class="rounded-tl-lg rounded-tr-lg bg-slate-400 p-0.5">
<p>Champion's Mastery</p>
</header>
<ul class="grid gap-1 rounded-bl-lg rounded-br-lg bg-slate-50 p-0.5">
<li v-for="champ in masteryData.slice(0,10)" :key="champ.championId">
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<img src="https://ddragon.leagueoflegends.com/cdn/12.5.1/img/champion/Karma.png" alt="" class="rounded-lg h-14 w-14">
<div>
<p class="font-medium text-center">{{ getChampionName(champ.championId) }}</p>
<p>Level {{ champ.championLevel }}</p>
</div>
</div>
<p class="text-2xl font-medium">{{ champ.championPoints }} Pts</p>
</div>
</li>
</ul>
</div>
</template>
<script>
import getChampionName from '#/api/search'
export default{
name: 'MasteryInfo',
props: [
'masteryData'
],
methods: {
getChampionName
}
}
</script>
But I'm getting this error Method "getChampionName" has type "undefined" in the component definition. and don't know what does it mean.
It seems you didn't import the method properly.
Change the import into:
import { getChampionName } from '#/api/search';
You can read more about import export in javascript here:
https://javascript.info/import-export
If you think you almost have the same question as this question, you can also refer to this:
Method "showDate" has type "undefined" in the component definition

Make comment specific to post

I rendered all of the posts using v-for, but now I want to add a comment section to each. I don't know how to make a connection between a comment and a post. As you can see in the code, the comment section renders for all post cards, but how can it be specific to each post? (I am not sure of the logic and how I can implement it).
<template>
<div v-for="item in userList" :key="item.uid">
<div class="row q-py-xs">
<q-card flat class="bg-grey-3 full-width user-card">
<q-card-section>
<div class="text-body1 q-ma-xs" id="postDtls">
{{ item.postDetails }}
</div>
<div class="row q-gutter-sm">
<q-chip square>
<q-avatar icon="home" color="amber-10" text-color="white" />
{{ item.postAddress }}
</q-chip>
</div>
</q-card-section>
<q-card-section> </q-card-section>
<q-card-section>
<q-input
outlined
id="cmInput"
v-model="cmOffer"
class="bg-grey-1"
color="amber-10"
label-slot
clearable
>
<template v-slot:label>
<span class="text-weight-bold text-deep-orange">write</span>
your comment
</template>
<template v-slot:append>
<q-btn
round
flat
icon="move_to_inbox"
v-on:click="submitComment()"
/>
</template>
</q-input>
<q-btn push dense class="full-width q-my-xs" color="amber-10" label="send message directly" />
</q-card-section>
</q-card>
</div>
</div>
</template>
<script>
import { ref } from "vue";
import { db } from "src/boot/firebase";
import { collection, query, where, getDocs } from "firebase/firestore";
import { doc, setDoc } from "firebase/firestore";
export default {
setup() {
return {
userList: ref([]),
cmList: ref([]),
idList: ref([]),
cmOffer: ref("")
};
},
async created() {
const querySnapshot = await getDocs(collection(db, "post"));
const postCm = await getDocs(collection(db, "post"));
querySnapshot.forEach(doc => {
let docData = doc.data();
this.userList.unshift(docData);
});
postCm.forEach(doc => {
let cmData = doc.data();
this.cmList.unshift(cmData);
});
},
};
</script>
You can create array of comments for every post, and then update them accordingly. Please take look at following snippet:
const { ref } = Vue
const app = Vue.createApp({
setup () {
const userList = ref([{uid: 1, postDetails: 'post 1', postAddress: '1', comments: ['comment 1', 'comment 2']}, {uid: 2, postDetails: 'post 2', postAddress: '2', comments: []}])
const cmOffer = ref([])
const submitComment = (id, i) => {
userList.value = userList.value.map(user => {
if(user.uid === id) {
user.comments.push(cmOffer.value[i])
}
return {...user}
})
cmOffer.value = []
}
return { userList, cmOffer, submitComment }
}
})
app.use(Quasar)
app.mount('#q-app')
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons" rel="stylesheet" type="text/css">
<link href="https://cdn.jsdelivr.net/npm/quasar#2.4.13/dist/quasar.prod.css" rel="stylesheet" type="text/css">
<div id="q-app">
{{ userList }}
<div v-for="(item, idx) in userList" :key="item.uid">
<div class="row q-py-xs">
<q-card flat class="bg-grey-3 full-width user-card">
<q-card-section>
<div class="text-body1 q-ma-xs" id="postDtls">
{{ item.postDetails }}
</div>
<div class="row q-gutter-sm">
<q-chip square>
<q-avatar icon="home" color="amber-10" text-color="white" />
{{ item.postAddress }}
</q-chip>
</div>
</q-card-section>
<q-card-section>
<div class="text-body1 q-ma-xs" v-for="(comment, i) in item.comments" :key="i" >
{{ comment }}
</div>
</q-card-section>
<q-card-section>
<q-input outlined id="cmInput" v-model="cmOffer[idx]" class="bg-grey-1" color="amber-10" label-slot clearable>
<template v-slot:label>
<span class="text-weight-bold text-deep-orange">write</span>
your comment
</template>
<template v-slot:append>
<q-btn round flat icon="move_to_inbox" #click="submitComment(item.uid, idx)" />
</template>
</q-input>
<q-btn push dense class="full-width q-my-xs" color="amber-10" label="send message directly" #click="submitComment(item.uid, idx)" />
</q-card-section>
</q-card>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#3/dist/vue.global.prod.js"></script>
<script src="https://cdn.jsdelivr.net/npm/quasar#2.4.13/dist/quasar.umd.prod.js"></script>

#click within v-for triggers all #click events

When a single #click is triggered from within the v-for the display property in all of the objects within the array is updated, rather than just by index. I only want that element that is click to receive the event and update the display property, not all of them.
<script>
import TransitionExpand from '#/transitions/transition-expand'
import ResourceCard from '#/components/resource-card'
export default {
components: {
TransitionExpand,
ResourceCard
},
data () {
return {
}
},
methods: {
toggle (evt, i) {
this.status[i].display = !this.status[i].display
}
},
async asyncData ({ app }) {
const { data } = await app.$axios.get('training_modules')
const status = Array(data.trainingModules.length).fill({ display: false })
return {
modules: data.trainingModules,
status
}
}
}
</script>
<template>
<div>
<div class="container px-4 mx-auto mt-16 lg:mt-32">
<div class="flex flex-wrap mb-20">
<h1 class="w-full lg:w-1/2 mb-6">Training Modules</h1>
<p class="w-full lg:w-1/2 leading-7 text-abbey"></p>
</div>
<div
v-for="(item, i) in modules"
:key="item.id"
class="mb-12"
>
<div class="flex items-center">
<h2>{{ item.title }}</h2>
<img #click="toggle($event, i)" class="ml-auto cursor-pointer" src="#/assets/images/icons/plus.svg" alt="open">
</div>
<TransitionExpand v-show="status[i].display">
<div class="">
<p class="mt-6 mb-12">{{ item.description }}</p>
<div class="flex flex-wrap -m-3">
<ResourceCard
v-for="resource in item.resources"
:key="resource.id"
:resource="resource"
/>
</div>
</div>
</TransitionExpand>
</div>
</div>
<BaseFooter />
</div>
</template>
Problem is in const status = Array(data.trainingModules.length).fill({ display: false }) code which is filing all array items with the same object which is observable hence change in display property will be applied to all elements in the array as status[0] === status[1].
Use map instead and it will work as expected:
const status = data.trainingModules.map(() => ({ display: false }));

Scroll down when clicked with Vue.js

What I want to do is that when the user clicks on an article, it scrolls down to a sibling section. Code looks something like this.
<template>
<article #click="handleCardClick" class="text-center mr-8 mb-12 cursor-pointer hover:opacity-50 w-1/5">
<picture class="flex justify-center items-center mb-4 w-full" style="height: 320px">
<img :src="source" :alt="imgAlt" class="shadow-md" style="" />
</picture>
<h4 class="font-bold mb-1">{{ title }}</h4>
<h6 class="text-sm text-gray-600">{{ tags.length > 0 ? tags[0].name : '' }}</h6>
</article>
</template>
<script>
import { mapActions, mapState } from 'vuex';
export default {
props: {
title: {
type: String,
required: true,
},
},
computed: {
...mapState({
previewIndex: state => state.templates.hasTemplate
}),
},
methods: {
...mapActions({
setActiveTemplate: 'templates/setActive',
setPreview: 'templates/setPreview',
removePreview: 'templates/removePreview',
}),
handleCardClick () {
this.setActiveTemplate(this.template);
this.selectTemplate(this.pos);
},
}
}
</script>
And the other file looks like this
<template>
<section v-if="template" class="flex justify-between w-full pt-10 pl-10 pr-5 pb-12 relative border-b border-t border-black my-4" style="height: 75vh">
<article class="flex flex-col justify-between" style="width: 25%">
<button #click="changeSection('invite')" class="h-1/3 pb-4">
<picture class="h-full w-full flex justify-center items-center bg-gray-100">
<img :src="template.url || ''" class="bg-gray-200 shadow-lg" style="min-height: 20px; min-width: 20px; height:80%" alt="Preview de la invitacion">
</picture>
</button>
</article>
</section>
</template>
I'm a bit new to Vue, so maybe it's really simple and I just can't find how to do it :) Thanks in advance!
You only need to assign a reference ref to each article and then build a method to go to any of your referenced articles:
<article #click="goto('art1')">Go to article 1</article>
For earch sibiling declare it's reference so you can call them on the goto method
<article ref="art1">
Article 1
</article>
Declare the goto method, it has a parameter, the reference of where you want to go.
methods: {
goto(refName) {
var element = this.$refs[refName];
var top = element.offsetTop;
window.scrollTo(0, top);
}
},
And this is it.
If you have the click action inside a child component then you'll have to use $emit to perform the click action on the parent, here is an example following the above:
Parent
<template>
<Article #scrollto="goto"></Article>
<Section ref="art1">
...
</Section>
</template>
<script>
import Article from "./article";
import Section from "./section";
export default {
methods: {
goto(refName) {
var element = this.$refs[refName];
var top = element.offsetTop;
window.scrollTo(0, top);
}
}
}
</script>
Article.vue
<template>
<div>
<button #click="$emit("scrollto", 'art1')">
Go to the Article!
</button>
</div>
</template>
Documentation about vue ref function
Documentation about window.scrollTo function

Categories

Resources