Check Firebase onAuthStateChanged before Vue app created in Nuxt JS - javascript

I have been using Vue JS v2 for some time now and I'm quite comfortable with it. Recently I have started using Nuxt JS and so far I was able to grasp most of the things. But after spending countless hours of searching the web, I am still unable to find out a way about how to use firebase onAuthStateChanged() before the Vue app gets created in Nuxt JS.
Let me explain a bit
In Vue 2 normally what I do is in main.js file
import Vue from 'vue'
import App from './App.vue'
import { auth } from '#/firebase/init'
let app = null
// ⚡ Wait for firebase auth to init before creating the Vue app
auth.onAuthStateChanged(() => {
// init app if not already created
if (!app) {
app = new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
}
})
But in Nuxt JS the entire main.js file is encapsulated. Now I know that I can use plugin to do stuff like Vue.use() but again those things are happening after the Vue app is created.
I want to wrap the vue app creation inside the firebase auth checking so I see no way to implement this. So, if anyone has any idea about how to implement this then please let me know.
Please note that I am not trying to do any redirection based on the auth state. I know those answers already exist here and I have checked them already.

I had the same problem and found an easier solution. However, I don't why this is not documented anywhere in firebase nuxt...
In the layouts/default.vue Layout I implemented the following:
<template>
<div>
<div v-show="!isLoaded" class="loading">
</div>
<div class="app" id="app" v-if="loaded">
<NavBar :loggedIn="loggedIn" />
<Nuxt id="router-view" />
<Footer />
</div>
</div>
</template>
<script>
export default {
data() {
return {
loggedIn: false,
loaded: false
}
},
created() {
this.$fire.auth.onAuthStateChanged(() => {
this.loaded = true
const user = this.$fire.auth.currentUser;
if (user) {
this.loggedIn = true
}
})
},
...
}
</script>
I think this works exactly like using it in the main.js in vueJS. By using v-if the main app content should only be executed once firebase auth is initialised.

I guess I have a similar problem and I managed to create a login-flow, but it feels like a workaround.
on login I store cookie and user data:
after login via firenbase I save userdata from authUser in vuex store via the onAuthStateChanged hook, which resides in a clientside only plugin.
store firebase token as cookie
on pageReload:
init store on serverside with data from cookie, with nuxtServerInit:
https://github.com/davidroyer/nuxt-ssr-firebase-auth.v2/blob/master/store/index.js
onAuthStateChanged is triggered and does nothing, because the userStore is already initialised
on redirect into app:
if user comes back into my app from payment service provider for example, the vuex store is empty until firebaseAuth plugin loads and saves userdata in vuex store. I use localStorage to persist the public user here, so the app thinks we are still logged in. https://www.npmjs.com/package/nuxt-vuex-localstorage
In the background onAuthStateChanged is triggered. This feels wrong.
Especially in the "on redirect into app" case it would be nice if onAuthStateChanged is handled first, because then we could skip localStorage and use access userData from firestore fore example without having to wait for the authPlugin to come up.
Maybe a solution could be to write a module, which offers more hooks you can use:
https://nuxtjs.org/guide/modules/#run-tasks-on-specific-hooks

Related

How to call a Cloud Function for Firebase in Vue3

I'm following the Net Ninja's tutorial about firebase cloud functions and I can't figure out how to call a function from a vue component. (he doesn't use vue in the tutorial).
So I have this vue3 app with firebase functions installed in it. How do I import the functions into a component? I can't find the answer anywhere. I'm still learning to code and I still don't understand very well how to import the packages needed.
I have a index.js file in the functions folder with a few functions inside.
I now want to call one of them ( a .onCall function) when clicking a button in a component.
I understand I need to import something in that component but I can't figure out what!
Your Vue.js app and your Cloud Functions for Firebase are totally different components of your whole application.
The Vue.js app is a front-end component (even if it is hosted in a cloud service like Firebase Hosting).
The Cloud Functions are serveless back-end components, hosted in the Firebase (Google Cloud) infrastructure and reacting to events.
To get these two components interacting with each other there are basically two possibilities:
For Callable Cloud Functions and HTTPS Cloud Functions, you will call them from your Vue.js app.
For background triggered Cloud Functions (e.g. triggered by a Firestore event like doc creation), the Vue.js front-end could generate the event (e.g. write to Firestore) and/or listen to the result of a Cloud Function execution (e.g. a Firestore doc is modified).
How do I import the functions into a component?
As explained above, you cannot integrate a Cloud Function code in your Vue.js code. In particular you cannot "import the functions into a component".
I now want to call one of them (a .onCall() function) when clicking a
button in a component.
If your Cloud Function is a Callable one, you can call it through the JS SDK as explained in the documentation.
UPDATE Following your comment:
As explained in the documentation, to call the Callable Function from your Vue.js app, you need to do as folloows (with the JS SDK v9):
Add Firebase to your Vue.js app. For example via a firebaseConfig.js file:
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";
import { getFunctions } from "firebase/functions";
const firebaseConfig = {
apiKey: "...",
// ....
};
const firebaseApp = initializeApp(firebaseConfig);
const db = getFirestore(firebaseApp);
const functions = getFunctions(firebaseApp);
export { db, functions };
Then, in your component, you do
<script>
import { functions } from '../firebaseConfig';
import { httpsCallable } from 'firebase/functions';
// ...
methods: {
async callFunction() {
const addMessage = httpsCallable(functions, 'addMessage');
const result = await addMessage({ text: messageText })
const data = result.data;
//...
});
}
</script>

How to use ffmpeg with Nuxt?

I am super new to Nuxt, and I am currently trying to move over a vue application that generates gifs ffmpeg.wasm to use Nuxt.js. However, whenever I visit the page the server crashes with the following error:
[fferr] requested a shared WebAssembly.Memory but the returned buffer is not a SharedArrayBuffer, indicating that while the browser has SharedArrayBuffer it does not have WebAssembly threads support - you may need to set a flag 18:36:38
(on node you may need: --experimental-wasm-threads --experimental-wasm-bulk-memory and also use a recent version)
I know it says to add the flags to node, as does the ffmpeg.wasm docs, but how do I do that via Nuxt? Or can I even do that? It is using the default dev server that comes with Nuxt, and I will be able to solve this when it's built and hosted but I need to have it locally as well.
Here is the component I am using in my barebones Vue app (stripped back but still causes an error). I am using node v14.17.6 and I'm using this library github.com/ffmpegwasm/ffmpeg.wasm/blob/master/README.md
<template>
<div class="home">
<h1>FFMPEG test</h1>
</div>
</template>
<script>
import { createFFmpeg } from '#ffmpeg/ffmpeg'
export default {
name: 'Home',
data: function () {
return {
ffmpeg: null,
}
},
created() {
this.ffmpeg = createFFmpeg({
log: true,
})
// Initialise loadFFmpeg
this.loadFFmpeg()
},
methods: {
async loadFFmpeg() {
await this.ffmpeg.load()
},
},
}
</script>
Creating the instance into a mounted() fixed the issue.
This is probably due to the fact that ffmpeg needed the Vue instance to be already mounted in the DOM (in the way it works).
created() is usually used for AJAX calls or things not so related to the Vue instance, and it being shown with the Composition API in their example gave me the idea of trying the mounted() hook.

Nuxt Dynamic Routing on Firebase

My Nuxt.js App has this structure:
/pages/index.vue
/pages/_slug/index.vue
When user gets /{any_page}, it will use the path to build the page content:
/pages/_slug/index.vue
<template>
<div>
{{slug}}
</div>
</template>
<script>
import fetch from 'isomorphic-fetch';
export default {
async asyncData({ params }) {
return { slug: params.slug }
}
}
</script>
This works perfectly when running the Nuxt App directly via yarn dev.
When I try to run this using firebase functions:
$ firebase serve --only functions,hosting
The static routes work perfectly, but the dynamic routes always render the default / page, instead of executing the dynamic one. How do I fix this?
If using nuxt generate, then the explanation below should work. If using nuxt build, look at this repo, it is a template i wrote for nuxt + firebase as ssr.
Generate says it skips over dynamic routes, unless you explicitly specify them, hence 404 errors. In your Nuxt config file, you want to specify those routes. Here is an example of fetching from the database during generation. Note that in my example below, the doc id is the slug for the page.
nuxt.config.js
generate: {
async routes() {
const { db } = require('./services/fireInit');
const qs = await db.collection('recipes').get();
return qs.docs.map(x => `/recipes/${x.id}`);
}
},
The downside to this approach is as you add new items via a CMS, you will need to rebuild/deploy your site each time if you want to be able to use these new links without a 404. CMS without redeploy does work if you navigate to new content 'organically'...404 only occurs on a page refresh or on jumping directly to the page from a hyperlink.

How to wait for Facebook SDK load in Vue.js Single-File Component

This is my first time to play with Vue.js but I need to use Facebook SDK for login.
Many documents emphasize that the Facebook SDK must first be loaded asynchronously.
I know there are a couple of Q&As on this topic at StackOverflow, but my case differs.
Using the created method while creating a new Vue instance
I cannot use this option because I'm using vue-router and only one component will use Facebook SDK for login.
Using nuxt.js
I don't know much about Vue.js yet. Trying nuxt.js seems like opening up a whole new can of worms.
Is there any example of loading Facebook SDK asynchronously in a Vue.js single-file component?
After doing the following you can call any of the SDK functions, like the login one, like this: Vue.FB.login(). Initialize the SDK before you call the Vue constructor. In lib/FacebookSdk.js:
const initFbSdk = (Vue, facebookClientId) => {
window.fbAsyncInit = () => {
FB.init({
appId: facebookClientId,
autoLogAppEvents: true,
xfbml: true,
version: 'v3.2',
});
Vue.FB = FB;
};
};
And in main.js:
import { initFbSdk } from './lib/FacebookSdk';
// ...
initFbSdk(Vue, process.env.VUE_APP_FACEBOOK_CLIENT_ID);
new Vue({
router,
store,
render: h => h(App),
}).$mount('#app');
Use it in your components or anywhere else like
import Vue from 'vue';
// ...
Vue.FB.login(resp => {
// ...
});

Pass data from blade file directly to Vue instance

I am working on a Vue application that's living in a Laravel project. I bind my vue instance to an id that's placed in a blade file.
What I would like to do is to pass the logged user to my Vue instance from Laravel/blade. Is there a way to do this? I know you can pass data through props but this here is just a regular div with an id of #root that's binding the Vue instance. I know how to get the logged user, but I am specific looking for an way to directly pass the data from blade to my vue instance.
app.js
// Require the deps from laravel (jQuery, axios, Bootstrap)
require('./bootstrap');
// Import vue deps
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter)
// Import the router & routes
import router from './routes'
// Init a new vue instance
const root = new Vue({
el: '#root',
data: {
User: name here..
},
router
});
Blade
#extends('layouts.app')
#section('content')
<!-- pass user below -->
<div id="root"></div>
#endsection
In your blade file, pass the logged user information to a javascript global variable. Let's say you just want the user id and name:
<script>
window.auth_user = {!! json_encode([
'id' => auth()->user()->id,
'name' => auth()->user()->name
]) !!};
</script>
Then in your javascript, you can access the auth_user global variable.
For example, to log the user name:
console.log(auth_user.name)
or
console.log(window.auth_user.name)
You have few options (I think I not list all) e.g:
You can pass data by converting them to json and write as HTML element or attribute and then read it from vue using e.g. document.querySelector(...) - more info here: Best way to store JSON in an HTML attribute?
You can change a littlebit architecture and create separate (Restful) API which your vue components will be use via ajax to read data (e.g. you can create GET api/v1/currentUser do read current logged user)
Completly change your architecture - to "microservices" - so in laravel only create Restful API, and creatte SEPEARATE project with vue (and NO laravel) user interface which use that API (it is modern approach to separation backend from frontend). You will face CORS problem in this approach but its no so hard (only at first time).
You might want to take a look at the PHP-Vars-To-Js-Transformer
package. You can use it either in your controller or in a #php directive.
Probably not a good practice with VueJS though.

Categories

Resources