I am trying to create an admin chat where the admin can view all the user messages and then reply to them. I've 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've tried 2 methods one of which 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've done the exact same thing to get the data with the only difference being that instead of doc(id)
having the id
argument for the route.params.id
I`ve 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>