I am trying to create and admin chat where the admin can view all the user messages and then reply to them I have been able to add documents to this collection but I am unable to display the messages so that the admin can read them all on the page. So far I have tried 2 methods one that I had used before for similar requests below.
import { projectFirestore } from "../Firebase/Config";
import { ref, watchEffect } from "vue"
const getChatMess = (id) => {
const ChatMess = ref([])
const error = ref(null)
let lastDoc = null;
const load = async () => {
try {
let query = projectFirestore.collection('ChatMess').doc(id).collection('Messages').orderBy('createdAt')
if (lastDoc) {
query = query.startAfter(lastDoc)
}
const res = await query.get()
if (!res.empty) {
lastDoc = res.docs[res.docs.length - 1]
// Add new results to exsiting ones
ChatMess.value = [...ChatMess.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 { ChatMess, error, load }
}
export default getChatMess
And another I made after reading through the firebase docs when I concole.log(ChatMess) I am able to see an array in the console log but it will not display the values since they are in the array.
import { projectFirestore } from "../Firebase/Config";
import { ref, watchEffect } from "vue"
const getChatMess = (id) => {
const ChatMess = ref(null)
const error = ref(null)
let collectionRef = projectFirestore.collection('Subscribed').doc(id).collection('messages').orderBy('createdAt')
const unsub = collectionRef.onSnapshot(snap => {
console.log(snap)
let results = []
snap.docs.forEach(doc => {
// must wait for the server to create the timestamp & send it back
// we don't want to edit data until it has done this
doc.data().createdAt && results.push({...doc.data(), id: doc.id})
});
// update values
ChatMess.value = results
error.value = null
console.log(ChatMess)
}, err => {
console.log(err.message)
ChatMess.value = null
error.value = 'could not fetch the data'
})
watchEffect((onInvalidate) => {
// unsub from prev collection when watcher is stopped (component unmounted)
onInvalidate(() => unsub());
});
return { ChatMess, error }
}
export default getChatMess
Both of these are in their own Javascript files which I then import the getChatMess to the page where I want the messages to be displayed this is shown below.
<script>
import { projectFirestore } from '../Firebase/Config'
import getChatMess from "../Composables/getChatMess";
import { ref } from 'vue'
import { timestamp } from '../Firebase/Config'
import { useRoute } from 'vue-router'
export default {
setup(){
const route = useRoute()
console.log(route)
console.log(route.params)
const { Chatmess, error } = getChatMess(route.params.id);
const message = ref('')
const Activate = ref(false)
const handleSubmit = async () => {
const NewChatmess = {
message: message.value,
createdAt: timestamp(),
}
const res = await projectFirestore.collection('Subscribed').doc(route.params.id).collection('messages').add(NewChatmess)
}
return { handleSubmit, Activate, message, Chatmess }
}
}
</script>
<template>
<div class="chat-window">
<div v-for =" doc in Chatmess" :key="doc.id">
<span class="created-at">{{ doc.createdAt }}</span>
<span class="name">{{ doc.name }}</span>
<span class="message">{{ doc.message }}</span>
</div>
<form @submit.prevent="handleSubmit">
<textarea
placeholder="Type a message and hit enter to send..."
v-model="message"
></textarea>
<button @click="Loading = true" class="bg-neutral-800 hover:bg-neutral-900 active:bg-neutral-800 text-red-700 hover:text-neutral-400 hover:scale-105 text-xl w-64 p-4 rounded-xl shadow cursor-pointer inline-block m-10 transition ease-in-out duration-300 drop-shadow-2xl"
>{{ Loading ? "Sending..." : "Send" }}</button>
<div v-if="error" class="error">{{ error }} blah</div>
</form>
</div>
</template>
I am not sure as to why the first Javascript file code isn't working I have done the exact same thing to get the data the only difference being that instead of doc(id) having the id argument for the route.params.id I have used
import { projectFirestore } from "../Firebase/Config";
import { ref } from "vue"
import { firebaseAuth } from "../Firebase/firebase";
const getChat = () => {
const Chat = ref([])
const error = ref(null)
let lastDoc = null;
const load = async () => {
try {
let query = projectFirestore.collection('Subscribed').doc(firebaseAuth.currentUser.uid).collection('messages').orderBy('createdAt')
if (lastDoc) {
query = query.startAfter(lastDoc)
}
const res = await query.get()
if (!res.empty) {
lastDoc = res.docs[res.docs.length - 1]
// Add new results to exsiting ones
Chat.value = [...Chat.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 { Chat, error, load }
}
export default getChat
And this has worked just fine in the past, I first assumed that the route.params.id was not working properly but I was able to console.log it as well as remove everything after .doc(id) and I was able to see that the route.params was working fine. Does anyone know how to do this?
I typically use the onSnapshot
method to listen for real-time updates.
import { projectFirestore } from "../Firebase/Config";
import { ref, watchEffect } from "vue";
const getChatMess = (id) => {
const ChatMess = ref([]);
const error = ref(null);
let collectionRef = projectFirestore.collection('Subscribed').doc(id).collection('messages').orderBy('createdAt');
const unsub = collectionRef.onSnapshot(snap => {
let results = [];
snap.docs.forEach(doc => {
doc.data().createdAt && results.push({ ...doc.data(), id: doc.id });
});
ChatMess.value = results;
error.value = null;
}, err => {
console.log(err.message);
ChatMess.value = null;
error.value = 'could not fetch the data';
});
watchEffect((onInvalidate) => {
onInvalidate(() => unsub());
});
return { ChatMess, error };
};
export default getChatMess;
Also fix the typo in your script where Chatmess
should be ChatMess
and make sure that the Chatmess
variable is correctly bound to the component template.
<script>
import { projectFirestore, timestamp } from '../Firebase/Config';
import getChatMess from "../Composables/getChatMess";
import { ref } from 'vue';
import { useRoute } from 'vue-router';
export default {
setup() {
const route = useRoute();
const { ChatMess, error } = getChatMess(route.params.id);
const message = ref('');
const handleSubmit = async () => {
const NewChatmess = {
message: message.value,
createdAt: timestamp(),
};
await projectFirestore.collection('Subscribed').doc(route.params.id).collection('messages').add(NewChatmess);
message.value = ''; // Clear the input after sending the message
};
return { handleSubmit, message, ChatMess, error };
}
};
</script>