Data not working in global scope but in local scope - javascript

I have been trying to make an e-commerce website and in that almost all things are done except for the database part. In which I have to fetch the data from firebase and use it in the products section.
I have fetched the data in local variables, however I want to store the data in a global scoped object, but it is not working.
So what I basically want is I want to fetch the data from firebase(DONE) and store it in global scope object(PENDING) so that I can use this object in other .js files.
Any kind of idea/advice/help/suggestion in more than welcomed!!
Thanks in advance!
This is my code:
https://jsfiddle.net/kumartanwar123/gseh4coq/
OR
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.6.7/firebase-app.js";
import { getAnalytics } from "https://www.gstatic.com/firebasejs/9.6.7/firebase-analytics.js";
// GLOBAL VARIABLES
var name, images, prices;
const firebaseConfig = {
apiKey: "--",
authDomain: "--",
databaseURL: "--",
projectId: "round-device-336118",
storageBucket: "round-device-336118.appspot.com",
messagingSenderId: "--",
appId: "--",
measurementId: "--",
};
const app = initializeApp(firebaseConfig);
firebase.initializeApp(firebaseConfig);
class firebase_class {
constructor(Images, Prices, Name) {
this.Images = Images;
this.Prices = Prices;
this.Name = Name;
}
}
// FIREBASE DATA FETCH
firebase
.database()
.ref("Practice")
.on("value", function (all_data) {
all_data.forEach(function (current_data) {
name = Object.keys(current_data.val().Name).map(
(keys) => current_data.val().Name[keys]
);
//In this if I use console.log(name) then it is working just fine, but not in outside the function.
images = Object.keys(current_data.val().Images).map(
(keys) => current_data.val().Images[keys]
);
prices = Object.keys(current_data.val().Prices).map(
(keys) => current_data.val().Prices[keys]
);
});
});
let firebase_object = new firebase_class(images, prices, name);

The problem is not where you access the database, but when you access it.
Data is loaded from Firebase (and pretty much all modern cloud APIs) asynchronously, and your main code (and thus the let firebase_object = new firebase_class(images, prices, name)) continues to execute while the data is loaded. Then when the data is available, your callback is invoked with it and it sets images, prices, and name.
And code that needs the data from Firebase has to be inside the callback, or be called from there. So:
// FIREBASE DATA FETCH
firebase
.database()
.ref("Practice")
.on("value", function (all_data) {
all_data.forEach(function (current_data) {
name = Object.keys(current_data.val().Name).map(
(keys) => current_data.val().Name[keys]
);
//In this if I use console.log(name) then it is working just fine, but not in outside the function.
images = Object.keys(current_data.val().Images).map(
(keys) => current_data.val().Images[keys]
);
prices = Object.keys(current_data.val().Prices).map(
(keys) => current_data.val().Prices[keys]
);
});
// 👇
let firebase_object = new firebase_class(images, prices, name);
... any use of firebase_object should be here too
});
Also check out:
Why Does Firebase Lose Reference outside the once() Function?

Related

firebase is not defined, database.ref() is not a function

i have this issue of 'firebase is not defined' when i try to read and display data from the relatime database.
I am using firebase to host a simple site in which you input 4 values and receive a score back, everything is scored in the database. i then want to display the highest 10 scores from the database, and im having trouble doing that.
Ive seen lot of people having this issue, but their resolutions are not working for me
Here is my code
function displayTopScores() {
// Reference to the scores in the database
var scoresRef = firebase.database().ref("scores");
// Retrieve the top 10 scores
scoresRef.orderByChild("Score").limitToFirst(10).on("value", function(snapshot) {
var scores = snapshot.val();
// Array to store the top 10 scores
var topScores = [];
// Add the scores to the array
for (var score in scores) {
topScores.push(scores[score]);
}
// Sort the scores in descending order
topScores.sort(function(a, b) {
return b.Score - a.Score;
});
// Create a table to display the scores
var table = document.createElement("table");
table.setAttribute("class", "table table-striped");
// Add a header row to the table
var headerRow = table.insertRow(-1);
var nameHeaderCell = headerRow.insertCell(-1);
nameHeaderCell.innerHTML = "Name";
var scoreHeaderCell = headerRow.insertCell(-1);
scoreHeaderCell.innerHTML = "Score";
// Add a row for each score
for (var i = 0; i < topScores.length; i++) {
var row = table.insertRow(-1);
var nameCell = row.insertCell(-1);
nameCell.innerHTML = topScores[i].Name;
var scoreCell = row.insertCell(-1);
scoreCell.innerHTML = topScores[i].Score;
}
// Add the table to the HTML page
var scoresContainer = document.querySelector("#scores-container");
scoresContainer.innerHTML = "";
scoresContainer.appendChild(table);
});
}
const dbRef = ref(getDatabase());
get(child(dbRef, `scores/Scores`)).then((snapshot) => {
if (snapshot.exists()) {
console.log(snapshot.val());
} else {
console.log("No data available");
}
}).catch((error) => {
console.error(error);
});
i have imported (i think) all the right things for this to happen, but i am clearly doing some mistake i have not figured out.
here is what i import:
<script type="module">
// Import the functions you need from the SDKs you need
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.15.0/firebase-app.js";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: "---",
authDomain: "---",
databaseURL: "---",
projectId: "---",
storageBucket: "---",
messagingSenderId: "---",
appId: "---",
measurementId: "---"
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
import {getDatabase, set, get, update, remove, ref, child}
from "https://www.gstatic.com/firebasejs/9.15.0/firebase-database.js";
const database = getDatabase();
I've read the documentation on how to import the database and app function, looked at other people with the same issue, tried to ask openAI but with no luck.
tried using different ways of creating a reference and read the data.
You need to pass the app variable as a parameter to getDatabase() it should be getDatabase(app)
Here is the official code from the firebase documentation:
import { initializeApp } from "firebase/app";
import { getDatabase } from "firebase/database";
const firebaseConfig = {
// ...
databaseURL: "https://DATABASE_NAME.firebaseio.com",
};
const app = initializeApp(firebaseConfig);
const database = getDatabase(app);
You can follow further examples from the documentation here: https://firebase.google.com/docs/database/web/start#web-version-9
function displayTopScores() {
const dbRef = ref(getDatabase());
get(child(dbRef, `scores/`)).then((snapshot) => {
if (snapshot.exists()) {
console.log(snapshot.val());
document.getElementById("scores-list").innerHTML = console.log(snapshot.val());
} else {
console.log("No data available");
}
}).catch((error) => {
console.error(error);
});
}
its not complete solution cause i still cant show in my site the values from the database, but this seems to fix the issue at least in part, now i see the database getting read from the chrome dev tool

"Error: Queue does not exist" when queue-ing task in Firebase Functions

I have got one functions that triggers on db update:
exports.eventAddedTrigger = functions
.region('europe-west6')
.firestore
.document('users/{user_id}/events/{event_id}')
.onCreate(async (snap, context) => {
const event = snap.data();
if (event) {
const { user_id, event_id } = context.params;
const queue = getFunctions().taskQueue('enrol');
const signupDate = DateTime.fromSeconds(event.signupDate.seconds).minus({minutes: 2});
const now = DateTime.local({zone: 'Europe/Zurich'})
let scheduleDelaySeconds = Math.floor(signupDate.diff(now, 'seconds').seconds);
if (scheduleDelaySeconds < 0) {
scheduleDelaySeconds = 10;
}
functions.logger.info(`Scheduling enrollment for ${signupDate.toISO()} in ${scheduleDelaySeconds} seconds`);
await queue.enqueue(
{ user_id, event_id },
{
scheduleDelaySeconds
}
)
}
});
This function triggers fine, but when it comes to enqueue-ing, I always get the following error
Error: Queue does not exist
regardless of whether I run the function emulated or in production.
The enrol function looks like this:
exports.enrol = functions
.region('europe-west6')
.runWith({
timeoutSeconds: 540,
memory: '1GB',
})
.tasks
.taskQueue()
.onDispatch(async (data) => {
const { user_id, event_id } = data.body;
await _enrol(user_id, event_id, db);
functions.logger.info(`Enrolled user ${user_id} to event ${event_id}`);
});
I have initialised my app correctly to my best knowledge:
initializeApp({
serviceAccountId: process.env.FIREBASE_SERVICE_ACCOUNT_ID,
});
Do I have to register the queue somewhere else?
I figured this out for me. If you're using non default region for your function (i.e. not us-central1) than you need to specify your queue name including your target region.
The schema is defined here https://github.com/firebase/firebase-admin-node/blob/master/src/utils/index.ts#L293
So use your enqueue function like this:
await this.functions
.taskQueue<INotification<any>[]>(`locations/${region}/functions/${queueName}`)
.enqueue(data);
I have tried to reproduce this issue and find out you need to initializeApp with the following syntax:
import {applicationDefault, initializeApp} from "firebase-admin/app";
initializeApp({
credential: applicationDefault(),
databaseURL: "https://<DATABASE_NAME>.firebaseio.com",
});
Where DATABASE_NAME will be databaseURL from your firebaseConfig in the console.You can get the database by visiting project settings under your apps section.
Plus you also have to enqueue the functions into queue as shown in the documentation, and make sure you enqueue your task in around 1 minute gap also you haven't included dispatchDeadlineSeconds in queue.enqueue like the one which is provided in above documentaions.
I have found the recommended doc which shows initialization of the SDK.

I cannot get data from firebase database

I am new to firebase. I'm trying to retreive data from a real time database using a node.js server sending associated credentials to firebase, but something gets broken after once('value') is called: its returned promise never gets resolved and server stops itself logging this message: "Process exited with code 3221226505".
I wrote the following code:
async function testFirebase1(firebaseCredentialsObj, path) {
let firebase = require('firebase')
firebase.initializeApp(firebaseCredentialsObj);
var database = firebase.database();
var ref = database.ref(path);
console.log(ref.toString());
try {
// Attempt 1
var prom = await ref.once('value');
const data = prom.;
console.log('data ' + data)
// Attempt 2
prom.then((snapshot) => {
console.log('snapshot ' + snapshot)
}).catch((error) => { console.log(error)} )
} catch (error) {
console.log(error)
}
}
No error ever gets catched.
I also tried to get data as an admin, but i got the same failing result
async function testFirebase3(firebaseCredentials, serviceAccountKey, databaseURL, path) {
const admin=require('firebase-admin');
const serviceAccount = serviceAccountKey;
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: databaseURL
});
var db=admin.database();
var userRef=db.ref(path);
const prom = await userRef.once('value');
console.log(prom)
}
Promise returned from once() method keep beeing pendent. This is its log:
[[PromiseStatus]]:'pending'
[[PromiseValue]]:undefined
Server is supposed to get databases'data in json format and send it to the client.
Why is this happening?
Based on your code, you are mixing traditional Promise chaining and async/await syntax together which is leading to your confusion.
Note: In the below snippets, I use the database query coding style I describe at the end of this answer.
SDK Initialization
To start with, in both testFirebase1 and testFirebase3, you initialize the default Firebase app instance in the function. If you call either function only once, you won't experience any problems, but any time you call them another time, they will always throw an error stating that the app has already been initialized. To solve this, you can lazily load these libraries using the following functions:
function lazyFirebase(options, name = undefined) {
const firebase = require('firebase');
// alternatively use the Promise-based version in an async function:
// const firebase = await import('firebase');
try {
firebase.app(name);
} catch (err) {
firebase.initializeApp(options, name);
}
return firebase;
}
function lazyFirebaseAdmin(options, name = undefined) {
const admin = require('firebase-admin');
// alternatively use the Promise-based version in an async function:
// const admin = await import('firebase-admin');
try {
admin.app(name);
} catch (err) {
const cred = options.credential;
if (typeof cred === "string") {
options.credential = admin.credential.cert(cred)
}
admin.initializeApp(options, name);
}
return admin;
}
Important Note: Neither of the above functions checks whether they use the same options object to initialize them. It just assumes they are the same configuration object.
Correcting testFirebase1
In testFirebase1, you are initializing the default Firebase app instance and then starting the process of the getting the data from the database. Because you haven't returned the promise from the ref.once('value') in the function, the caller will get a Promise<undefined> that resolves before the database call completes.
async function testFirebase1(firebaseCredentialsObj, path) {
let firebase = require('firebase')
// bug: throws error if initializeApp called more than once
firebase.initializeApp(firebaseCredentialsObj);
// bug: using `var` - use `const` or `let`
var database = firebase.database();
var ref = database.ref(path);
console.log(ref.toString());
try {
// Attempt 1
// bug: using `await` here, makes this a DataSnapshot not a Promise<DataSnapshot>
// hence `prom` should be `snapshot`
// bug: using `var` - use `const` or `let`
var prom = await ref.once('value');
// bug: syntax error, assuming this was meant to be `prom.val()`
const data = prom.;
console.log('data ' + data)
// Attempt 2
// bug: a `DataSnapshot` doesn't have a `then` or `catch` method
// bug: if `prom` was a `Promise`, you should return it here
prom
.then((snapshot) => {
console.log('snapshot ' + snapshot)
})
.catch((error) => {
console.log(error)
})
} catch (error) {
console.log(error)
}
}
Correcting these problems (and making use of my coding style when dealing with RTDB queries) gives:
async function testFirebase1(firebaseCredentialsObj, path) {
const firebase = lazyFirebase(firebaseCredentialsObj);
const snapshot = await firebase.database()
.ref(path)
.once('value');
// returns data at this location
return snapshot.val();
}
Correcting testFirebase3
In testFirebase3, you are initializing the default Firebase Admin app instance and correctly waiting for the data from the database. Because you haven't returned the data from the database, the caller will get a Promise<undefined> that resolves when the database call completes but without the containing data.
async function testFirebase3(firebaseCredentials, serviceAccountKey, databaseURL, path) {
const admin = require('firebase-admin');
// note: unnecessary line, either call `serviceAccountKey` `serviceAccount` or use `serviceAccountKey` as-is
const serviceAccount = serviceAccountKey;
// bug: throws error if initializeApp called more than once
// bug: `firebaseCredentials` is unused
// note: when initializing the *default* app's configuration, you
// should specify all options to prevent bugs when using
// `admin.messaging()`, `admin.auth()`, `admin.storage()`, etc
// as they all share the default app instance
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: databaseURL
});
// bug: using `var` - use `const` or `let`
var db=admin.database();
var userRef=db.ref(path);
// bug: using `await` here, makes this a DataSnapshot not a Promise<DataSnapshot>
// hence `prom` should be `snapshot`
const prom = await userRef.once('value');
// bug: logging a `DataSnapshot` object isn't useful because it
// doesn't serialize properly (it doesn't define `toString()`,
// so it will be logged as "[object Object]")
console.log(prom)
}
Correcting these problems (and making use of my coding style when dealing with RTDB queries) gives:
async function testFirebase3(firebaseCredentials, serviceAccountKey, databaseURL, path) {
const admin = lazyFirebaseAdmin({
...firebaseCredentials, // note: assuming `firebaseCredentials` is the complete app configuration,
credential: serviceAccountKey,
databaseURL: databaseURL
});
const snapshot = await admin.database()
.ref(path)
.once('value');
return snapshot.val();
}

Why are my JavaScript Date/Time objects stored as maps as opposed to timestamps in Cloud Firestore?

The attached image is a screenshot of two "date/time" entries in my Firestore document.
timeCompleted is a value that I entered directly in the database, choosing "timestamp" as the field's type. Per the screenshot, you can tell that the date/time shows in a readable format (not that it matters to me).
timeCreated is a value added by my JavaScript Reactjs application, using firebase.firestore.Timestamp.fromDate(new Date(myDate + " " + myTime)), as prescribed by the Google docs. As opposed to being stored as a timestamp type, it is stored as a map containing nanoseconds and seconds.
Is there a way to store my JavaScript date (timeCreated) as a true timestamp, similarly to timeCompleted?
My firebase class (gets imported through React's Context API)
import app from "firebase/app";
import "firebase/firestore";
import "firebase/functions";
const config = {
apiKey: ...,
authDomain: ...,
databaseURL: ...,
projectId: ...
};
class Firebase {
constructor() {
app.initializeApp(config);
this.auth = app.auth();
this.firestore = app.firestore;
this.functions = app.functions;
}
}
export default Firebase;
My React Component
import React, { useState, useEffect, useContext } from "react";
import { FirebaseContext } from "../../Firebase";
const InnerComponent = React.memo(props => {
const firebase = useContext(FirebaseContext);
//Calls Google Cloud function below
firebase.functions().httpsCallable("storeInDb")({
const myDate = "2019-02-25";
const myTime = "17:45";
orderDetails {
name: "someName",
timeCreated: firebase.firestore.Timestamp.fromDate(new Date(myDate + " " + myTime))
}
)}
My Google Cloud Function
exports.storeInDb = functions.https.onCall(async (data, context) => {
return id = await orderCreation(data.orderDetails);
});
const orderCreation = async orderDetails => {
try {
const docRef = await admin.firestore()
.collection(pathToCollection...)
.add(orderDetails);
return docRef.id;
} catch (error) {
console.log("ORDER CREATION ERROR", error);
}
};
It looks like you're expecting the type of the Timestamp object that you're passing into the callable function to be retained between the client and server. Unfortunately, it doesn't work that way. When a Timestamp object is serialized, it just converts to a JavaScript object with seconds and nanoseconds properties. The callable function on the other side isn't going to automatically reconstitute that object into a Timestamp. In fact, all types are lost, and everything is just converted to JSON.
What you're going to have to do is take that serialized object in the function and turn it back into a Timestamp yourself before you pass it to the Firestore SDK. Then, the SDK will arrange for Firestore to save it as a timestamp type field. This means you're going to have to do some initial processing on the data parameter and make sure it has all the correct values to write into Firestore.
Again, if it's not completely clear - the type of the object must be correct with the Firestore SDK calls. You can't pass arbitrary objects and expect it to do smart conversions.

Cannot query from Firebase for my React Native app?

It seems I can retrieve all my data from my Firebase Realtime Database with the following:
let itemsRef = firebaseApp.database().ref('/');
itemsRef.on('value', (snapshot) => {
let data = snapshot.val();
let item = Object.values(data);
this.setState({item});
});
But when I try to query with specific parameters instead of just retrieving all the information, I always end up with this error:
#firebase/database: FIREBASE WARNING: Exception was thrown by user callback.
TypeError: Cannot convert undefined or null to object
This is how I'm trying to query information... literally following the documentation, and somehow my code works when I retrieve all information but not when the only change I make is adding a query?
let itemsRef = firebaseApp.database().ref('/');
itemsRef.orderByChild('roundedBPM').equalTo('100').on('value', (snapshot) => {
let data = snapshot.val();
let item = Object.values(data);
this.setState({item});
});
Is anyone else having problems querying from Firebase or am I doing something wrong?
This is how my data is structured on Firebase:
I would try initializing firebase in dbConfig.js for instance like so:
import * as firebase from 'firebase';
let config = {
apiKey: "XXXXXXX",
authDomain: "XXXXX",
databaseURL: "XXXXX",
projectId: "XXXXX",
storageBucket: "XXXX",
messagingSenderId: "XXXX"
};
firebase.initializeApp(config);
export default firebase;
I would import firebase where it's:
import firebase from './dbConfig.js'
let itemsRef = firebase.database().ref('/');
itemsRef.orderByChild('roundedBPM').equalTo('100').on('value', (snapshot) => {
let data = snapshot.val();
let item = Object.values(data);
this.setState({item});
});
Note: OrderByChild the element roundedBPM needs to be a direct child to the root path '/' if it's a nested child you could do something like this:
let itemsRef = firebase.database().ref('/users');
itemsRef.orderByChild('roundedBPM').equalTo('100').on('value', (snapshot) => {
...
});
Note: if you are setting equalTo() roundedBPM when it's a number and not a string you wouldn't get back any data. Make sure you use the correct type of data.
I hope this will help!

Categories