invalid Firebase binding source with vuefire - firebase

I'm starting with vue and firebase, but now I have this error when I show what I already have in my database.
main.js
import vueFire from 'vuefire';
import firebase from 'firebase';
Vue.use(vueFire);
let config = {
apiKey: "mykey",
authDomain: "mydomain",
databaseURL: "myurl",
projectId: "my",
storageBucket: "bucket",
messagingSenderId: "number"
};
let application = firebase.initializeApp(config)
let db = application.database()
let notificationsdb = db.ref('notifications')
export { notificationsdb };
component.vue
import { notificationsdb } from '../main';
export default {
name: 'Notifications',
firebase: {
notifi: notificationsdb
},
data() {
return{
newNoti: {
name: '',
text: ''
},
}
},
methods: {
addNoti: function(){
notificationsdb.push(this.newNoti);
this.newNoti.name = '',
this.newNoti.text = ''
toastr.success('Notificación creada');
},
deleteNoti: function(noti){
notificationsdb.child(noti['.key']).remove();
toastr.success('Notificación eliminada');
}
}
}
If I delete this line of code and save it and then put it back, I keep changes, it works. But if I press F5 it stops working
firebase: {
notifi: notificationsdb
},
and he sent me the following error
[Vue warn]: Error in created hook: "Error: VueFire: invalid Firebase binding source."

So, I'm assuming you started your project with something like:
vue init webpack myProject
Basically what's happening is your component doesn't have access to the data from firebase the first time you load it. It takes some time for you to make an edit (time in which the server request is completing). Then when you press save, it triggers the HMR, and your site reloads with the data it's expecting.
Try making these changes (although you should move this config stuff into a separate file (like firebaseDB.js for example)):
// let db = application.database()
// let notificationsdb = db.ref('notifications')
// export { notificationsdb }
export default application.database()
Then in component.vue:
[...]
import db from '../firebaseDB'
[...]
firebase: {
notifi: db.ref('notifications')
},
[...]
You'll probably want to add a loading state var to this component, and so on. Good luck!

Related

How to use Firebase Cloud FireStore Database in a Next JS project. How to initialize it correctly?

On a firebase.js file I am doing this:
import firebase from "firebase/app";
import "firebase/firestore";
const firebaseConfig = {
apiKey: process.env.APIKEY,
authDomain: process.env.AUTHDOMAIN,
databaseURL: process.env.DATABASEURL,
projectId: process.env.PROJECTID,
storageBucket: process.env.STORAGEBUCKET,
messagingSenderId: process.env.MESSAGINGSENDERID,
appId: process.env.APPID,
measurementId: process.env.MEASUREMENTID
};
export function firebaseDB() {
// Initialize Firebase
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
// firebase.analytics();
}
return firebase;
}
Then, on pages/index.js I am using the getInitialProps:
App.getInitialProps = async () => {
const firebaseDatabase = await firebaseDB();
const db = firebaseDatabase.firestore();
let result;
db.collection("users")
.add({
first: "Ada",
last: "Lovelace",
born: 1815
})
.then(function(docRef) {
console.log("Document written with ID: ", docRef.id);
result = { docs: docRef };
})
.catch(function(error) {
console.error("Error adding document: ", error);
result = { error: error };
});
return result
};
I am assuming that because of the asynchronous nature I am returning the result variable undefined and getting this error:
"App.getInitialProps()" should resolve to an object. But found "undefined" instead.
So, I am not happy with the way I am configuring this...can someone throw some light?
Here are some of the ways I can think of:
Create an HOC for it and wrap the page components that will be using it. (https://medium.com/#uvictor/simple-firebase-authentication-with-next-js-using-hoc-higher-order-components-8e8931d25cfa)
Initialise it in the Root component and pass the DB ref to the children. For example, in your Root component, you declare your routes there. What you wanna do is pass the DB ref to each of the components under it. Though this might be problematic when you wanna do SSR. Not sure how would this play out.
If only a single page will be using Firebase (happened to me a couple of times), just do it like what you are doing right now.
If you are thinking of using Redux, you might want to initialise firebase and bind it to the store (https://github.com/prescottprue/react-redux-firebase)
My suggestion is, try not to overcomplicate it. Do what works for you.

How to import Firebase only on client in Sapper?

I'm importing Firebase into my Sapper application, I do not want the imports to be evaluated on the server. How do I make sure imports are only on the client-side?
I am using Sapper to run sapper export which generates the static files. I have tried:
Creating the firebase instance in it's own file and exported the firebase.auth() and firebase.firestore() modules.
Trying to adjust the rollup.config.js to resolve the dependencies differently, as suggested from the error message below. This brings more headaches.
Creating the Firebase instance in client.js. Unsuccessful.
Creating the instance in stores.js. Unsuccessful.
Declaring the variable and assigning it in onMount(). This causes me to have to work in different block scopes. And feels a bit hacky.
The initialization of the app, works fine:
import firebase from 'firebase/app'
const config = {...}
firebase.initializeApp(config);
I have also discovered that if I change the import to just import firebase from 'firebase' I do not get this server error:
#firebase/app:
Warning: This is a browser-targeted Firebase bundle but it appears it is being run in a Node environment. If running in a Node environment, make sure you are using the bundle specified by the "main" field in package.json.
If you are using Webpack, you can specify "main" as the first item in
"resolve.mainFields": https://webpack.js.org/configuration/resolve/#resolvemainfields
If using Rollup, use the rollup-plugin-node-resolve plugin and set "module" to false and "main" to true: https://github.com/rollup/rollup-plugin-node-resolve
I expected to just export these firebase functionalities from a file and import them into my components like:
<script>
import { auth } from "../firebase";
</script>
But as soon as that import is include, the dev server crashes. I don't want to use it on the server, since I'm just generating the static files.
Does anyone have some ideas on how to achieve importing only on client side?
So I have spent too much time on this. There isn't really a more elegant solution than onMOunt.
However, I did realize that sapper really should be used for it's SSR capabilities. And I wrote an article about how to get set up on Firebase with Sapper SSR and Cloud Functions:
https://dev.to/eckhardtd/how-to-host-a-sapper-js-ssr-app-on-firebase-hmb
Another solution to original question is to put the Firebase CDN's in the global scope via the src/template.html file.
<body>
<!-- The application will be rendered inside this element,
because `app/client.js` references it -->
<div id='sapper'>%sapper.html%</div>
<!-- Sapper creates a <script> tag containing `app/client.js`
and anything else it needs to hydrate the app and
initialise the router -->
%sapper.scripts%
<!-- Insert these scripts at the bottom of the HTML, but before you use any Firebase services -->
<!-- Firebase App (the core Firebase SDK) is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/6.0.4/firebase-app.js"></script>
<!-- Add Firebase products that you want to use -->
<script src="https://www.gstatic.com/firebasejs/6.0.4/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/6.0.4/firebase-firestore.js"></script>
</body>
</html>
and in the component:
<script>
import { onMount } from 'svelte';
let database, authentication;
onMount(() => {
database = firebase.firestore();
authentication = firebase.auth();
});
const authHandler = () => {
if (process.browser) {
authentication
.createUserWithEmailAndPassword()
.catch(e => console.error(e));
}
}
</script>
<button on:click={authHandler}>Sign up</button>
I was able to import firebase using ES6. If you are using rollup you need to consfigure namedExports in commonjs plugin:
//--- rollup.config.js ---
...
commonjs({
namedExports: {
// left-hand side can be an absolute path, a path
// relative to the current directory, or the name
// of a module in node_modules
'node_modules/idb/build/idb.js': ['openDb'],
'node_modules/firebase/dist/index.cjs.js': ['initializeApp', 'firestore'],
},
}),
The you can use it like this:
//--- db.js ---
import * as firebase from 'firebase';
import 'firebase/database';
import { firebaseConfig } from '../config'; //<-- Firebase initialization config json
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
export { firebase };
// Initialize db
export const db = firebase.firestore();
and maybe use it in a service like such:
// --- userService.js ----
import { db } from './common';
const usersCol = db.collection('users');
export default {
async login(username, password) {
const userDoc = await usersCol.doc(username).get();
const user = userDoc.data();
if (user && user.password === password) {
return user;
}
return null;
},
};
EDITED
Full rollup config
/* eslint-disable global-require */
import resolve from 'rollup-plugin-node-resolve';
import replace from 'rollup-plugin-replace';
import commonjs from 'rollup-plugin-commonjs';
import svelte from 'rollup-plugin-svelte';
import babel from 'rollup-plugin-babel';
import { terser } from 'rollup-plugin-terser';
import config from 'sapper/config/rollup';
import { sass } from 'svelte-preprocess-sass';
import pkg from './package.json';
const mode = process.env.NODE_ENV;
const dev = mode === 'development';
const legacy = !!process.env.SAPPER_LEGACY_BUILD;
// eslint-disable-next-line no-shadow
const onwarn = (warning, onwarn) =>
(warning.code === 'CIRCULAR_DEPENDENCY' && warning.message.includes('/#sapper/')) || onwarn(warning);
export default {
client: {
input: config.client.input(),
output: config.client.output(),
plugins: [
replace({
'process.browser': true,
'process.env.NODE_ENV': JSON.stringify(mode),
}),
svelte({
dev,
hydratable: true,
emitCss: true,
preprocess: {
style: sass(),
},
}),
resolve({
browser: true,
}),
commonjs({
namedExports: {
// left-hand side can be an absolute path, a path
// relative to the current directory, or the name
// of a module in node_modules
'node_modules/idb/build/idb.js': ['openDb'],
'node_modules/firebase/dist/index.cjs.js': ['initializeApp', 'firestore'],
},
}),
legacy &&
babel({
extensions: ['.js', '.mjs', '.html', '.svelte'],
runtimeHelpers: true,
exclude: ['node_modules/#babel/**'],
presets: [
[
'#babel/preset-env',
{
targets: '> 0.25%, not dead',
},
],
],
plugins: [
'#babel/plugin-syntax-dynamic-import',
[
'#babel/plugin-transform-runtime',
{
useESModules: true,
},
],
],
}),
!dev &&
terser({
module: true,
}),
],
onwarn,
},
server: {
input: config.server.input(),
output: config.server.output(),
plugins: [
replace({
'process.browser': false,
'process.env.NODE_ENV': JSON.stringify(mode),
}),
svelte({
generate: 'ssr',
dev,
}),
resolve(),
commonjs(),
],
external: Object.keys(pkg.dependencies).concat(require('module').builtinModules || Object.keys(process.binding('natives'))),
onwarn,
},
serviceworker: {
input: config.serviceworker.input(),
output: config.serviceworker.output(),
plugins: [
resolve(),
replace({
'process.browser': true,
'process.env.NODE_ENV': JSON.stringify(mode),
}),
commonjs(),
!dev && terser(),
],
onwarn,
},
};
The clean way is to use the Dynamic Import as the documentation said: Making a component SSR compatible
The way to get around this is to use a dynamic import for your component, from within the onMount function (which is only called on the client), so that your import code is never called on the server.
So here for example we want to import the core of firebase and the authentication package too.
<script>
let firebase;
onMount(async () => {
const module = await import("firebase/app");
await import("firebase/auth");
firebase = module.default;
firebase.initializeApp(firebaseConfig);
});
<script>
And now you can use firebase object as you can, for example we want to login with email and password:
let email;
let password;
async function login() {
try {
let result = await firebase.auth().signInWithEmailAndPassword(
email,
password
);
console.log(result.user);
} catch (error) {
console.log(error.code, error.message);
}
}
In order to use Firebase with Sapper, you have to import firebase not firebase/app. You do want firebase to be able to load correctly with SSR on the backend, not just the frontend. If you have some metatags, for example, that would be stored in the database, you want them to load on the backend (UNTESTED).
You could just use firebase, but then you get the annoying console warning. Remember also firebase loads ALL firebase dependencies while firebase/app does not, that is why you don't want to use it on the frontend. There is probably a way with admin-firebase, but we want to have less dependencies.
Do not use rxfire at all. You don't need it. It causes errors with Sapper. Just plain Firebase.
firebase.ts
import firebase from 'firebase/app';
import "firebase/auth";
import "firebase/firestore";
import * as config from "./config.json";
const fb = (process as any).browser ? firebase : require('firebase');
fb.initializeApp(config);
export const auth = fb.auth();
export const googleProvider = new fb.auth.GoogleAuthProvider();
export const db = fb.firestore();
Firebase functions require an extra step and you must enable dynamic imports. (UNTESTED)
export const functions = (process as any).browser ? async () => {
await import("firebase/functions");
return fb.functions()
} : fb.functions();
While this compiles, I have not tried to run httpsCallable or confirmed it will load from the database on the backend for seo ssr from the db. Let me know if it works.
I suspect all of this will work with the new SvelteKit now that Sapper is dead.

acces denied when going to write data database in firebase

Problem:
I am creating an expo app with firebase integration. In there I have created a config file to connect with database. This is how it looks.
import Firebase from 'firebase';
let config = {
apiKey: "mykey",
authDomain: "mytrain-5beba.firebaseapp.com",
databaseURL: "https://mytain-5beba.firebaseio.com",
projectId: "mytain-5beba",
storageBucket: "mytain-5beba.appspot.com",
messagingSenderId: "myid"
};
let app = Firebase.initializeApp(config);
export const db = app.database();
In a services folder, I have created a service file like this to handle crud operations. This how it looks.
import { db } from '../config/db';
export const addItem = (item) => {
db.ref('/tains').push({
name: item
});
}
In my component In the componentDidMount method, I have done something like this to test weather database connection is working properly.
import React, { Component } from "react";
import {
StyleSheet,
Text,
TextInput,
View,
TouchableOpacity,
Dimensions,
Picker,
ListView
} from "react-native";
import {
Ionicons,
Foundation,
Entypo,
MaterialCommunityIcons,
FontAwesome,
MaterialIcons
} from "#expo/vector-icons";
import { Autocomplete } from "react-native-autocomplete-input";
import { addItem } from '../../services/stationService';
const { height, width } = Dimensions.get("window");
const box_width = width / 2 + 40;
export default class TicketForm extends React.Component {
constructor(props) {
super(props);
this.state = {
name: ""
};
}
static navigationOptions = {
title: "",
headerStyle: {
backgroundColor: "#2b78fe"
},
headerTintColor: "#fff",
headerTitleStyle: {
color: "#ffff"
}
};
componentDidMount() {
const name = "Tharindu";
addItem(name);
}
render() {
return (
<View>
<Text>Haiii</Text>
</View>
);
}
}
});
When I run expo start --android. It showing me some warnings like this.
[15:52:56] [2018-11-05T10:22:51.070Z] #firebase/database:, FIREBASE WARNING: set at /station/-LQYOMpnDzgdkj25XUDQ failed: permission_denied
- node_modules\react-native\Libraries\ReactNative\YellowBox.js:80:15 in warn
- node_modules\expo\src\Expo.js:26:41 in warn
- ... 20 more stack frames from framework internals
[15:53:01] [Unhandled promise rejection: Error: PERMISSION_DENIED: Permission denied]
- node_modules\#firebase\database\dist\index.cjs.js:13076:45 in <unknown>
- node_modules\#firebase\database\dist\index.cjs.js:690:8 in exceptionGuard
- ... 18 more stack frames from framework internals
[15:53:04] Setting a timer for a long period of time, i.e. multiple minutes, is a performance and correctness issue on Android as it keeps the timer module awake, and timers can only be called when the app is in the foreground. See https://github.com/facebook/react-native/issues/12981 for more info.
(Saw setTimeout with duration 360595ms)
- node_modules\react-native\Libraries\ReactNative\YellowBox.js:80:15 in warn
- node_modules\expo\src\Expo.js:26:41 in warn
- ... 11 more stack frames from framework internals
[15:59:03] Setting a timer for a long period of time, i.e. multiple minutes, is a performance and correctness issue on Android as it keeps the timer module awake, and timers can only be called when the app is in the foreground. See https://github.com/facebook/react-native/issues/12981 for more info.
(Saw setTimeout with duration 92226ms)
- node_modules\react-native\Libraries\ReactNative\YellowBox.js:80:15 in warn
- node_modules\expo\src\Expo.js:26:41 in warn
- ... 11 more stack frames from framework internals
I am very new to these react-native and firebase stuff.Can someone help me to solve this problem.Thank you!
Open firebase, select database on the left hand side.
Now on the right hand side, select [Realtime database] from the dropdown and change the rules to:
{
"rules": {
".read": true,
".write": true
}
}
it workss..!!

Vue/Firestore/Firebase error on retrieve data

I am creating a Vue JS app with Firestore database but have a problem somewhere in the Firestore import (probably).
Its a simple app just storing some employee details which want to be displayed (initially) to test its working. (It doesnt!) Its just using "firebase": "^5.0.4", not vue-firebase or other plugin.
Its Firestore not the Firebase Real Time db.
So in the firebaseInit.js config file are all the basic config options which are as below
import * as firebase from 'firebase'
// Initialize Firebase
var config = {
apiKey: "AIzaSyCyKS3QxqtR9HvetpT2vWKFNxa_yeRKdhA",
authDomain: "vuefsprod-fc778.firebaseapp.com",
databaseURL: "https://vuefsprod-fc778.firebaseio.com",
projectId: "vuefsprod-fc778",
storageBucket: "vuefsprod-fc778.appspot.com",
messagingSenderId: "1048509841840"
}
firebase.initializeApp(config)
var auth = firebase.auth()
var db = firebase.database()
export function signOut (callback) {
auth.signOut().then(value => {
callback()
}, err => { callback(err) })
}
export default 'firebase/firestore'
And then the script snippet to test it is as below (in Helloworld.vue)
import db from '../firebaseInit'
export default {
name: 'home',
data () {
return {
employees: [],
loading: true
}
},
created() {
db.collections('employees').get().then(querySnapshot => {
querySnapshot.forEach(doc => {
console.log(doc)
const data = {
}
})
})
}
}
Yarn compiles the app which displays, but there is a warning error in console as below
[Vue warn]: Error in created hook: "TypeError: __WEBPACK_IMPORTED_MODULE_0__firebaseInit__.a.collections is not a function"
found in
---> <Home> at src/components/HelloWorld.vue
and no data is displayed to the console, there are 6 items in the employees collection.
I'm also wondering where the "a" in a.collections comes from.
Any tips on this or a better way of doing it, say with vue-firebase or other, are more than welcome. Screenshot below.
Many Thanks
You are declaring the database with the Real Time Database (RTDB) service, instead of the Firestore service:
var db = firebase.database() // <- RTDB
You should do the following instead:
var db = firebase.firestore()
Since the RTDB does not have collections, you receive the error "collections is not a function"
FYI, the different available services are documented here: https://firebase.google.com/docs/web/setup#use_firebase_services, together with how to access/declare them.
The issue is on how you retrieving data from the firebase db
db.collections('employees')
Kindly update it to
db.collection('employees')
This should resolve this .

(Webpack failing to build?) Firebase DB in different levels of Vue components

I'm trying to set up a vue-fire app using single file Vue components.
I'm using the standard (full) Vue-cli Webpack template available on the official site.
I have firebase loaded in App.vue like this:
let config = {
...
};
let app = Firebase.initializeApp(config);
let db = app.database();
let usersRef = db.ref('users');
...
export default {
name: 'app',
data () {
return {
login: {
email: '',
password: ''
},
newUser: {
email: '',
password: ''
},
showRegister: false
}
},
firebase: {
users: usersRef,
},
...
}
I'm using Vue-router and my routes are set up like this:
import Vue from 'vue'
import Router from 'vue-router'
import Home from '#/components/Home'
import News from '#/components/News'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/news',
name: 'News',
component: News
}
]
})
I would like to be able to access my Firebase app in the 'News' component. The problem is that if I include the entire Firbase setup in the News.vue file, I get the error:
[DEFAULT]: Firebase: Firebase App named '[DEFAULT]' already exists (app/duplicate-app).
The recommended solution is to export the initialized app's database in App.vue and import it in the child component. So I add this to the bottom of my App.vue script:
module.exports.FBApp = app.database();
And this to News.vue:
import FBApp from '../App.vue'
let usersRef = FBApp.ref('users')
But now I am getting the following error:
TypeError: __WEBPACK_IMPORTED_MODULE_0__App_vue___default.a.ref is not a function
Does anyone know how to do this? Surely it can't be too hard.
Create a db.js file like the following alongside app.vue.
import firebase from 'firebase'
var config = {
apiKey: 'xxxxx'
authDomain: 'xxxxx'
databaseURL: 'xxxxx'
projectId: 'xxxxx'
storageBucket: 'xxxxx'
messagingSenderId: 'xxxxx'
}
const firebaseApp = firebase.initializeApp(config)
const db = firebaseApp.database()
export default db
In your main.js:
import Vue from 'vue'
import App from './App'
import router from './router'
import VueFire from 'vuefire'
// explicit installation required in module environments
Vue.use(VueFire)
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
})
And now in any component, eg:
<template>
<span>
{{ news }}
</span>
</template>
<script>
import db from '../db'
export default {
data: function () {
return {
users: [],
sample: []
}
},
firebase: function () {
return {
news: db.ref('news')
}
}
}
</script>

Resources