Nuxt this.$route is undefined - firebase

I have a dynamic route called products/_id which should load an product from Firestore. I am using VueFire to fetch the product from the Firebase database. Fetching the whole products collection works perfectly fine.
firestore: {
product: firebase
.firestore()
.collection("products")
}
But getting the document with a certain id is where the trouble begins. Normally I would call
firestore: {
product: firebase
.firestore()
.collection("products")
.doc(this.$route.params.id),
}
But now I get an error saying "TypeError: Cannot read property '$route' of undefined". Which is strange because if i log the route to the console I can actually see the id. Moreover, calling ".doc("DOCUMENT_ID") work perfectly fine.
Anyone knows how to fix this? I've tried a combination of methods and mounted hooks to retrieve the id but to no avail.
mounted() {
this.getProduct();
},
methods: {
getProductId() {
return this.$route.params.id;
},
},
firestore: {
products: firebase.firestore().collection("products").doc(this.getProductId),
}
Brings up the same error...

firestore is specified as an object here, so it's evaluated immediately outside the context of the component, so this would not be the Vue component instance.
To resolve this, specify firestore as a function that returns the intended object, as shown in the docs:
export default {
firestore() {
// ✅ `this` is Vue component instance inside function
return {
products: firebase.firestore().collection("products").doc(this.getProductId),
}
}
}

Related

How do I read session properties using useSession() in getServerSideProps() using nextjs and next-auth?

I am trying to only show data where the "Heading" is the same as the logged-in user's name. I'm using Next.js and I'm trying to server render the data from my Prisma database, but when I try to filter the data to only show "Segments" where the "Heading" is the same as the currently logged-in user it throws an error:
Server Error
TypeError: Cannot read properties of null (reading 'useContext')
I am using Next-Auth for user authentication and previously I have use this method to access the currently logged in user's data:
import { useSession } from "next-auth/react";
.
.
.
const { data: session } = useSession();
.
.
.
<div>User name: {session.user.name}</div>
But when I use getServerSideProps() that method doesn't work.
This is what I tried so far:
export async function getServerSideProps() {
const { data: session } = useSession();
const segments: Segment[] = await prisma.segment.findMany({
where: {
Heading: session.user.name,
},
});
return {
props: {
initialSegments: segments,
},
};
}
But it shows me the error shown above.
useSession is unavailable on the server side according to next-auth's docs.
You may be able to use unstable_getServerSession(), however do note that it's experimental and its API may change in the future (as noted in the docs).

Nuxt plugin not available in Vuex's 'this' in Firebase production (ERROR: this.$myPlugin is not a function)

I'm trying to upload my nuxt app to Firebase as cloud function. The problem is that in the nuxtServerInit action I'm trying to call a plugin function, which apparently is not yet defined at that moment, because an error is thrown: (ERROR: this.$myPlugin is not a function). The code works in dev mode, it's just after upload to Firebase it fails.
The setup is as follows:
myPlugin.js
let env, auth, app, $store;
export default (context, inject) => {
env = context.app.context.env;
auth = context.app.$fire.auth;
app = context.app;
$store = context.store;
inject('myPlugin', myPlugin);
};
async function myPlugin(...) {... }
nuxt.config.js
plugins: [
{ src: '~/plugins/myPlugin', mode: 'all' }, // with no mode specified it fails too
],
vuex index.js
export const actions = {
async nuxtServerInit({ dispatch, commit }, { req }) {
const tl = await dispatch("initAction");
return tl;
}
}
vuex someModule.js
const actions = {
initAction({ commit }) {
return this.$myPlugin(...).then(...) // this line throws '$myPlugin is not a function' error
}
}
What can be the reason for the different behaviour in dev and in prod modes and how could I fix the problem?
UPDATE:
After further testing I established that the problem is not caused by the nuxtServerInit timing. I moved the call of the initAction from nuxtServerInit to a page's created hook. However the same error appears: this.$query is not a function.
The problem occured, because js files were not getting fully loaded due to CORB errors caused by incorrect configuration. Details described in this question.

Binding VuexFire to a collection filtered with a query

I'm unsuccessfully trying to bind a Vuex state attribute to a queried collection in FireStore. I was wondering if anyone with more experience could point me in the right direction. This is what I'm currently doing:
In a Vuex Module called auth I'm declaring the following bind to userArticles
export const bindUserArticles = firestoreAction(({ bindFirestoreRef }, id) => {
return bindFirestoreRef('userArticles', userCollectionRef('articles', id))
})
This in turn points to a firebase method for querying the data (which works)
export const userCollectionRef = (collectionName, id) => {
return firestore().collection(collectionName).where("author.idAuthor", "==", id)
}
And I'm importing and dispatching the method in my Vue file in the following way
computed: {
...mapGetters('user', ['currentUser']),
},
methods: {
...mapActions('articles', ['bindUserArticles']),
},
watch: {
currentUser () {
this.bindUserArticles(this.currentUser.id)
}
}
So when the currentUser is updated upon login the method is triggered. The method is triggered and the right id is being sent, I've tested it with console.log. There is no error being displayed. When I try for example to modify the idAuthor of an existing article in the database, the list userArticles does not update. When I try adding or deleting an article from the database that has the specific idAuthor, the list userArticles does not update. I've also tried placing the this.bindUserArticles(this.currentUser.id) in the created() and mounted() life-cycle, to no avail.Does anyone have a clue where I'm going wrong about this?
Thanks in advance

Decrease response time in Firebase Vue app when liking a post

I have an app with different 'procedures' (think posts or pages), which one can like. Currently the process works: Tap like => run method "likeProcedure" => run dispatch action "likeProcedure" => update UI. It usually happens almost immediately, but sometimes there's a lag that gives this a "non-native" feel. Is there some sort of way that I could return feedback immediately, while stile holding single origin of truth on the firebase database?
Thank you!
Page Code:
<v-icon
v-if="!userProfile.likedProcedures || !userProfile.likedProcedures[procedure.id]"
color="grey lighten-1"
#click="likeProcedure({ id: procedure.id })"
>
mdi-star-outline
</v-icon>
and
computed: {
...mapState(["userProfile"]),
procedures() {
return this.$store.getters.getFilteredProcedures();
},
},
Vuex code:
async likeProcedure({ dispatch }, postId) {
const userId = fb.auth.currentUser.uid;
// update user object
await fb.usersCollection.doc(userId).update({
[`likedProcedures.${postId.id}`]: true,
});
dispatch("fetchUserProfile", { uid: userId });
},
Side note: I'm trying to remove the dispatch("fetchUserProfile") command, but this doesn't work, because then I'm calling dispatch without using it. And I cannot remove dispatch because then the object calling it is empty. And I cannot remove the object, because then the argument ('postId') isn't working. So if anyone knows how to deal with that, that would be extremely helpful.
Thank you :)
So this is the best solution I've come up yet. It kind of destroys the idea of a single source of truth, but at least it provides an immediate UI update:
async likeProcedure({ dispatch, state }, postId) {
console.log("likeProcedure");
const userId = fb.auth.currentUser.uid;
// line below provides immediate update to state and hence to the UI
state.userProfile.likedProcedures[postId.id] = true;
// line below updates Firebase database
await fb.usersCollection.doc(userId).update({
[`likedProcedures.${postId.id}`]: state.userProfile.likedProcedures[
postId.id
],
});
// line below then fetches the updated profile from Firebase and updates
// the profile in state. Kind of useless, but ensures that client and
// Firebase are in-sync
dispatch("fetchUserProfile", { uid: userId });
},
async fetchUserProfile({ commit }, user) {
// fetch user profile
const userProfile = await fb.usersCollection.doc(user.uid).get();
// set user profile in state
commit("setUserProfile", userProfile.data());
// change route to dashboard
if (router.currentRoute.path === "/login") {
router.push("/");
}
},

Vuejs & Firestore - How to Update when Data Changes in Firestore

I've gone through a bunch of tutorials and docs but cannot seem to be able to update on page when data changes in Firestore (NOTE: not Firebase)
Heres what I have currently which is working fine except if data changes in the DB it is not reflected on the page itself unless I refresh. Code below is within script tags:
import { recipeRef } from '../../firebase';
export default {
data() {
return {
recipes: []
}
},
firestore: {
recipes: recipeRef
},
created() {
db.collection('recipes').get().then((onSnapshot) => {
this.loading = false
onSnapshot.forEach((doc) => {
let data = {
'id': doc.id,
'name': doc.data().name
}
this.recipes.push(data)
})
})
}
I'm not using Vuex. Adding data, editing and reading works fine. Just not reflecting changes once data has changed. Maybe there is a life cycle hook Im supposed to be using? For "onSnapshot" - Ive tried "snap", "querySnapshot" etc. No luck.
Thanks in advance.
Remove the get() and just replace with snapshot - like so
created() {
db.collection('recipes').onSnapshot(snap => {
let foo = [];
snap.forEach(doc => {
foo.push({id: doc.id, name: doc.data().name})
});
}
});
I am not familiar with the firestore API, but glancing through the docs, it looks like calling get() is how you query a single time. Where you have onSnapshot should really be querySnapshot -- that is, the results of a one query. See:
https://firebase.google.com/docs/firestore/query-data/get-data
versus:
https://firebase.google.com/docs/firestore/query-data/listen
So to get live updates, it looks like you need to create a listener, like so:
db.collection('recipes')
.onSnapshot(function(snapshot) {
snapshot.forEach(function(doc) {
// Find existing recipe in this.recipes
// and swap in the new data
});
}, function(error) {
// handle errors
});
I think you will need to add that listener in addition to the get() query you are currently doing. Hope this helps!

Resources