Firestore pagination and Vue - firebase

I am trying to create Infinite scrolling pagination with Vuejs and Firestore.
So far I have been able to get the code to work in the sense that it is fetching and showing the data as intended. However when the new query is constructed after scrolling down to the bottom, the original query with the same values loads again.
It seems that I am doing something wrong with the startAfter() method, which is supposed to get the next values in the firestore query.
This is my setup:
setup() {
const latestDoc = ref(null);
const getFoods = ref([]);
onMounted(() => {
runQuery(latestDoc.value);
});
const runQuery = async (doc) => {
let q = query(collection(db, "foods"), orderBy("title"), startAfter(doc), limit(5));
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
getFoods.value.push({ ...doc.data(), id: doc.id });
});
};
const loadData = (InfiniteScrollCustomEvent) => {
InfiniteScrollCustomEvent.target.complete();
latestDoc.value = getFoods.value.length - 1;
runQuery(latestDoc.value);
};
return { getFoods, optionsOutline, loadData, latestDoc, runQuery };
},
Can anyone tell me what I am doing wrong?

Related

How to get all items from subcollection Firebase Firestore Vue

How do I get all the comments from the subcollection?
This is mine reusable function to get comments collection.
import { ref, watchEffect } from 'vue';
import { projectFirestore } from '../firebase/config';
const getCollection = (collection, id, subcollection) => {
const comments = ref(null);
const error = ref(null);
// register the firestore collection reference
let collectionRef = projectFirestore
.collection(collection)
.doc(id)
.collection(subcollection);
const unsub = collectionRef.onSnapshot(
snap => {
let results = [];
snap.docs.forEach(doc => {
doc.data().createdAt && results.push(doc.data());
});
// update values
comments.value = results;
error.value = null;
},
err => {
console.log(err.message);
comments.value = null;
error.value = 'could not fetch the data';
}
);
watchEffect(onInvalidate => {
onInvalidate(() => unsub());
});
return { error, comments };
};
export default getCollection;
And this is mine Comments.vue where i passing arguments in setup() function (composition API)
const { comments } = getAllComments('posts', props.id, 'comments');
When i console.log(comments) its null, in snapshot doc.data() is good but somehow results too is empty array even if i push doc.data() to results array and pass it to comments.value.
Can someone help me how to get that subcollection?
This is my Comment.vue component
export default {
props: ['id'],
setup(props) {
const { user } = getUser();
const content = ref('');
const { comments } = getAllComments('posts', props.id, 'comments');
const ownership = computed(() => {
return (
comments.value && user.value && user.value.uid == comments.value.userId
);
});
console.log(comments.value);
}
return { user, content, handleComment, comments, ownership };
},
};
const getCollection = (collection, id, subcollection) => {
const comments = ref(null);
const error = ref(null);
// Firestore listener
return { error, comments };
}
The initial value of comments here is null and since Firebase operations are asynchronous, it can take a while before the data loads and hence it'll log null. If you are using comments in v-for then that might throw an error.
It'll be best if you set initial value to an empty array so it'll not throw any error while the data loads:
const comments = ref([]);
Additionally, if you are fetching once, use .get() instead of onSnapshot()

react native data show on console.log but not on screen

Sometimes the app working well.
but I dont know why, sometimes the data from firebase give me blank screen instead of the data.
after I reopen the app it work.
for example,
one of my pages:
useEffect( () => {
const subscriber = firestore()
.collection('Trails')
.onSnapshot(querySnapshot => { //const querySnapshot = await firebase.firestore().collection('users').get();
const trails = [];
console.log('subscribe')
if (querySnapshot)
querySnapshot.forEach(async documentSnapshot => {
trails.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
console.log("trails test", trails)
});
setTrails(trails);
setLoading(false);
});
return () => {subscriber()};
}, []);
I made useEffect to get the data from DB then show it, and same - sometimes give me blank and sometimes works well.
I want to publish the app but im not satisfying with this bug?
sorry for my English, I dont even know how to describe this problem better.
Please can anyone guide me through? maybe my useEffect not doing well?
I think you should use debugging.
React native documentation
Stackoverflow question
I think there's issue with the return in useEffect return is called when componeent unmounts. here's an example how i handle async data fetching:
...
const [data, setData] = useState([]);
const isCurrentView = useRef(true);
useEffect(() => {
if (isCurrentView.current === true) {
const asyncFetch = async () => {
const response = await fetch(...something) //here some Asynchronous Call Like(axios, fetch)
setData([...response]);
};
asyncFetch();
}
return () => {
isCurrentView.current = false;
};
}, []);
...
im not 100% sure if this is the VERY best approach, but i have seen similar code in places so i addopted this.
problem was solved:
the setTrails was under scope and it kept refreshing with empty data.
querySnapshot.forEach(async documentSnapshot => {
trails.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
setTrails(trails); // <<<~~~~ put the set in this scope.
});
setLoading(false);
});

Nuxtjs getting firestore data within asyncData

I'm trying to convert my VueJS app to NuxtJS to work with SSR. I'm stuck trying to get data loaded with asyncData. When I add my query to a 'mounted() {}' function it works fine but I can't get it to work with asyncData(){} so that I can use SSR.
Does anyone have any idea how to fix this.
My code:
<ul>
<li v-for='province in provinces' v-bind:key="province.id"> {{province.name_nl}}</li>
</ul>
asyncData () {
return { msg: 'Welcome to my new app' }
const moment = require("moment");
var date = moment(new Date()).format("YYYY-MM-DD");
let housesArray = []
let provincesArray = []
return firebase.firestore()
.collection('provinces')
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
provincesArray.push(doc.data());
});
return {provinces: provincesArray}
});
},
Or is there another way I should be doing this? Keeping in mind that it does have to work with SSR.
PS: Yes this code is inside my pages folder, not the component, I know that's not allowed.
When using asyncDate() you can add the async and await keywords to wait for the response(s) before returning them.
How I solved it:
async asyncData () {
const moment = require("moment");
var date = moment(new Date()).format("YYYY-MM-DD");
let housesArray = []
let provincesArray = []
await firebase.firestore()
.collection('provinces')
.orderBy('name_nl')
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
provincesArray.push(doc.data());
});
});
await firebase.firestore()
.collection("houses")
.where("valid_until", ">", date)
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
housesArray.push(doc.data());
});
});
return {
provinces: provincesArray,
houses: housesArray
}
},

what should I do If I want to do nothing in the one of my execution path in Background trigger cloud function?

as far as I know, background trigger cloud function should return a promise,right? but what if I want to do nothing in the one of my execution path ?
export const updateDataWhenUserUnattendTheEvent = functions.firestore
.document('events/{eventId}/Attendee/{userId}')
.onDelete((snap, context) => {
const eventID = context.params.eventId
const eventRef = snap.ref.firestore.collection('events').doc(eventID)
const db = admin.firestore()
return db.runTransaction(async t => {
const doc = await t.get(eventRef)
if (doc) {
const eventRankPoint = doc.data().rankPoint
let eventCapacity = doc.data().capacity
return t.update(eventRef,{
isFullCapacity : false,
capacity : eventCapacity + 1,
rankPoint: eventRankPoint - 1
})
} else {
// what should I write in here? empty promise?
return new Promise()
}
})
})
I want to my function worked only if the document is exist. so what should I do ? I write new Promise but .... I don't know what to do actually. thanks in advance
You can just return null if there's no asynchronous work to perform in some code path of your functions. You only truly need a promise if it tracks some async work.
Alternatively, you could return a promise that's resolved immediately with Promise.resolve(null)
Because db.runTransaction is an async function it will return a Promise all the time.
You can drop the else statement and the method will perform as expected because runTransaction will return Promise<void> which is a valid response for Cloud Functions
export const updateDataWhenUserUnattendTheEvent = functions.firestore
.document('events/{eventId}/Attendee/{userId}')
.onDelete((snap, context) => {
const eventID = context.params.eventId;
const eventRef = snap.ref.firestore.collection('events').doc(eventID);
const db = admin.firestore();
return db.runTransaction(async t => {
const doc = await t.get(eventRef);
if (doc) {
const eventRankPoint = doc.data().rankPoint;
let eventCapacity = doc.data().capacity ;
return t.update(eventRef,{
isFullCapacity : false,
capacity : eventCapacity + 1,
rankPoint: eventRankPoint - 1
});
}
});
});
You can also make the onDelete function async which means you can force it to always return a Promise - the below is valid and will exit the function correctly.
export const updateDataWhenUserUnattendTheEvent = functions.firestore
.document('events/{eventId}/Attendee/{userId}')
.onDelete(async (snap, context) => {
// Do Nothing
return;
});

How can I get specific document data from firestore querysnapshot?

I got a querysnapshot in a function.
And want to bring the whole querysnapshot to another function (functionTwo).
In functionTwo, I want to get a specific document in the querysnapshot WITHOUT forEach. The specific doc can be changed by different cases.
ref_serial_setting.get()
.then(querysnapshot => {
return functionTwo(querysnapshot)
})
.catch(err => {
console.log('Error getting documents', err)
})
let functionTwo = (querysnapshot) => {
// getting value
const dataKey_1 = "dataKey_1"
// Tried 1
const value = querysnapshot.doc(dataKey_1).data()
// Tried 2
const value = querysnapshot.document(dataKey_1).data()
// Tried 3 (Put 'data_name': dataKey_1 in that doc)
const value = querysnapshot.where('data_name', '==', dataKey_1).data()
}
The result are all these trying are not a function.
How can I get specific document data from querysnapshot??
or
Is there any easy method to change the querysnapshot to JSON?
You can get an array of the document snapshots by using the docs property of a QuerySnapshot. After that you'll have to loop through getting the data of the doc snapshots looking for your doc.
const docSnapshots = querysnapshot.docs;
for (var i in docSnapshots) {
const doc = docSnapshots[i].data();
// Check for your document data here and break when you find it
}
Or if you don't actually need the full QuerySnapshot, you can apply the filter using the where function before calling get on the query object:
const dataKey_1 = "dataKey_1";
const initialQuery = ref_serial_setting;
const filteredQuery = initialQuery.where('data_name', '==', dataKey_1);
filteredQuery.get()
.then(querySnapshot => {
// If your data is unique in that document collection, you should
// get a query snapshot containing only 1 document snapshot here
})
.catch(error => {
// Catch errors
});
Theres an easy way to do this, each QuerySnapshot has a property docs which returns an array of QueryDocumentSnapshots. See QuerySnapshot documentation.
let citiesRef = db.collection('cities');
let query = citiesRef.where('capital', '==', true).get().then(snapshot => {
snapshot.docs[0]; // => returns first document
});
let citiesRef = db.collection('cities');
let query = citiesRef.where('capital', '==', true).get()
.then(snapshot => {
if (snapshot.empty) {
console.log('No matching documents.');
return;
}
snapshot.forEach(doc => {
console.log(doc.id, '=>', doc.data());
});
})
.catch(err => {
console.log('Error getting documents', err);
});
from https://firebase.google.com/docs/firestore/query-data/get-data
you can use this code :
const querySnapshot = await getDocs(collection(db, "collectionNaame"));
const docSnapshots = querySnapshot.docs;
for (var i in docSnapshots) {
console.log(i)
const doc = docSnapshots[i].data();
console.log(doc)
Just do
db.doc(<<ref>>).get()
this returns a promise
[here ]: https://firebase.google.com/docs/firestore/query-data/get-data#get_a_document is the link to the docs

Resources