Array of Empty Objects vs Array of Objects - Firebase and Polymer - javascript

I'm trying to use the Polymer Shop template to create an online store, replacing the standard category objects with objects from Firebase Cloud Firestore. After initializing the database, I'm trying to use the objects to show a list of categories in a drawer menu.
This top example is with Cloud Firestore. As well as the code, you can see via screenshot what the console prints out when categoryList is console logged.
Cloud Firestore Console Output
let categoryList = []
firebase.firestore().enablePersistence()
.then(function() {
// Initialize Cloud Firestore through firebase
var db = firebase.firestore();
db.collection("product-categories").where('active', '==', true)
.get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
// doc.data() is never undefined for query doc snapshots
categoryList.push(doc.data())
});
})
.catch(function(error) {
console.log("Error getting documents: ", error);
});
});
Here's the code in the original Polymer Shop template, as well as a screenshot showing the output when categoryList is printed to the console.
Polymer Shop Template Console Output
(function() {
let categoryList = [
{
name: 'oils_and_tinctures',
title: 'Oils and Tinctures'
},
{
name: 'concentrates',
title: 'Concentrates'
},
{
name: 'Vape',
title: 'Vape'
},
{
name: 'topicals',
title: 'Topicals'
},
{
name: 'pet_products',
title: 'Pet Products'
}
];
It seems like I need an array of empty of objects and then to fill those objects. How do I get the data from Firebase Cloudstore to match the format of the original template data?
Thanks in advance for anyone's help!

querySnapshot.forEach(function (doc) => {
categoryList.push({ name: doc.name, title: doc.title })
}).
OR
function CategoryData(name, title) {
this.title = title;
this.name = name;
}
querySnapshot.forEach(function (doc) => {
categoryList.push(new CategoryData(doc.name, doc.title))
})
Using the second way you can define what ever structure you like.

Related

How to extract data from a nested map in firestore?

I have a collection users and it has multiple documents. Inside each document, there is a field called contacts. It is of map type. Inside contacts, I have another map-type data.
My database:
I am trying to store my contacts data in contactDetailsArr array like:
[{userId: GA3yfqLaaTaDugSrFOQujnj34Y13,
lastMessage:"Here there!!!",
time: t {seconds: 1663220632, nanoseconds: 36000000}
},
{userId: TZjQb8yoYfQbowloQk1uLRCCPck1,
lastMessage:"How are you?",
time:t {seconds: 1663306634, nanoseconds: 859000000}
}]
but I am getting my array as
[{userId: GA3yfqLaaTaDugSrFOQujnj34Y13,
lastMessage:undefined,
time:undefined
},
{userId: TZjQb8yoYfQbowloQk1uLRCCPck1,
lastMessage:undefined,
time:undefined
}]
Here is my code:
export const getUserContacts= () =>{
const contactDetailsArr=[];
db.collection("users").doc(userId).get() //userId is equal to "3aTGU..."
.then(docs=>{
const contactsObject= docs.data().contacts;
for(let contact in contactsObject){
contactDetailsArr.push({userId: contact,
lastMessage: contact.lastMsg,
time: contact.lastMsgTime
})
}
})
.catch(err=>{
console.log(err);
})
}
If maps are objects then why I am able to extract data as we do in the case of objects.
Please guide what I am doing wrong.
If you log the values in your for loop, it is easy to see the problem:
for(let contact in contactsObject){
console.log(contact);
}
This logs:
0
1
So you need to still look up the object at the index:
for(let i in contactsObject){
console.log(contactsObject[i]);
}
Or with a forEach list comprehension:
contactsObject.forEach((contact) => {
console.log(contact, contact.userId, contact.lastMessage, contact.time);
}

How to update document data which located inside a subcollection in Firebase & Nuxt

My firestore structure
Subwork Package (collection)
- UID (document)
- Subwork Registration (subcollection)
- Doc unique ID (document)
- data etc etc
- UID (document)
I have a table that displays all the documents that I have, inside the "Subwork Registration" subcollection.
//LOAD DATA TO TABLE IS SUCCESS
initializeTable() {
this.SubworkTable = [];
firestore
.collectionGroup("Subwork Registration")
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
this.SubworkTable.push({ ...doc.data(), id: doc.id })
})
})
}
Then, I will pick one of the docs and edit/update its content from the list of docs that shows on the table. But when I do as below, I got into an error .collectionGroup("").doc("") is not a function kind of error
data: () => ({
editedItem: {
v-model: '',
v-model: '',
// etc etc
}
})
//METHODS: {}
UpdateTable() {
var data = {
//
};
firestore
.collectionGroup("Subwork Registration")
.doc(this.editedItem.id)
.update(data)
.then(() => {
//
})
}
I do take note that we cannot update or set if we do .collectionGroup("Subwork Registration").doc() but Im not sure how to do it the right way (to edit/update document inside a subcollection).
Any advice?
To write a document in Firestore you will need to know the complete path to that document.
I recommend either keeping the DocumentReference if you can (as that already contains the entire path to the document), or alternative store the doc.ref.path for each document instead of doc.id as you do now.

forEach not a function when index starts at anything but 0

When inside an object there isn't an index that starts with 0, it returns:
TypeError: data.forEach is not a function
This is how the database looks:
If I add an object with the index 0 to the database, like so (my formatting doesn't matter, this is just to illustrate the hierarchy):
0: {
email: "testmail",
uid: "testuid"
}
Suddenly the forEach function works and also retrieves the users with index 3 and 4. How can I make the forEach loop start at index 3 for example? Or is there a different method that I should be using instead? My code:
useEffect(() => {
if(props.klasData.gebruikers !== undefined) {
var data = props.klasData.gebruikers;
data.forEach(function (user) {
if(!emails.hasOwnProperty(user.email)) {
addEmail(oldArray => [...oldArray, user.email]);
}
setPending(false)
})
}
}, []);
Edit
props.klasData.gebruikers returns all keys within the "gebruikers" object with their children.
It looks like your data is being interpreted as an array by the Firebase SDK, since it starts with sequential numeric keys. If you print the value of your gebruikers snapshot, you'll see that it's:
[null, null, null, {email: "test", uid: "test"}, {email: "wouter#...", uid: "..."}]
These null values are added by the Firebase SDK to turn the keys into a proper array.
If you want to keep the Firebase SDK from converting the data into an array, prefix the keys with a non-numeric character. For example:
"key2": {email: "test", uid: "test"},
"key3": {email: "wouter#...", uid: "..."}
In general, it is more idiomatic to use the UID of the users as the keys in a collection of users. That way, you won't need to query for the UID, and you're automatically guaranteed that each user/UID can only be present in the collection one.
I changed the database like you can see here, as Frank van Puffelen suggested. As Frank also predicted, the root of my problem was coming from the function I didn't post.
By transforming all the indexes of UIDs I was fetching from the database to sequential numeric keys, I managed to get the forEach function working. I did this using users = Object.values(users).filter((el) => {return el != null}). The full effect hook can be found below:
useEffect(() => {
var refCodes = firebase.database().ref("userdata/" + currentUser.uid + "/docentCodes").orderByKey();
refCodes.once("value").then(function (snapshotCodes) {
snapshotCodes.val().forEach(function (code) {
var refCodeData = firebase.database().ref("klassencodes/" + code).orderByKey();
refCodeData.once("value").then(function (snapshotCodeData) {
var users = snapshotCodeData.val().gebruikers;
users = Object.values(users).filter((el) => {return el != null})
if(snapshotCodeData.val() !== null) {
setUsercount(oldArray => [...oldArray, users.length]);
setKlasData(oldArray => [...oldArray, snapshotCodeData.val()]);
setUserdata(oldArray => [...oldArray, users]);
addCode(oldArray => [...oldArray, code])
}
setPending(false);
})
})
})
}, []);
In the function where this useEffect is used, I added const [userdata, setUserdata] = React.useState([]); to acommodate this new information stripped down from indexes of UIDs to indexes made of numeric keys. This userdata is exported to another function, which has the effect hook as stated in the original question. I changed this up to be:
useEffect(() => {
if(props.userData !== undefined) {
var data = props.userData;
if(data !== undefined) {
data.forEach(function (user) {
if(!emails.hasOwnProperty(user.email)) {
addEmail(oldArray => [...oldArray, user.email]);
addUID(oldArray => [...oldArray, Object.keys(props.klasData.gebruikers)]);
}
setPending(false)
})
}
}
}, []);
Summary
In retrospect, I should've gone with a seperate const for just the userdata (snapshotCodeData.val().gebruikers), seperate from the other data returned from the snapshot (snapshotCodeData.val()).
I hope this may help you. The golden line of code is users = Object.values(users).filter((el) => {return el != null}).

res.json() sends data in the form of res.data but accessing _id within it is undefined

I'm having trouble understanding why a res.json call in my app sends data (an order object) but when I try accessing a piece of that data (res.data._id) and storing it into a variable I get it as undefined. I know this piece of data exists since the console.log shows the order object's _id value but console.logging that particular piece returns undefined. What causes this behavior?
Backend logic:
router.post("/new", function(req, res) {
const productInfo = req.body.productInfo;
let order = new Order();
order.product = {
_id: productInfo.id,
name: productInfo.name,
description: productInfo.description,
price: productInfo.price,
quantity: productInfo.quantity
}
order.status = "Created";
order.total = productInfo.price * productInfo.quantity;
order.owner = {
id: req.body.id,
username: req.body.username
}
order.save().then(order => {
res.status(200).json(`Order created successfully! Created order details: ${order}`);
}).catch(err => {
console.log("Order create error: ", err.message);
});
});
Frontend logic:
let orderID = "";
return (
<PayPalButton
createOrder={(data, actions) => {
axios.post("http://localhost:4000/orders/new",
{productInfo, userID, username}
).then((res) => {
if(res.status === 200) {
console.log(res.data);
console.log(res.data._id)
orderID = res.data._id;
}
}).catch((err) => {
console.log(err);
});
return actions.order.create({
purchase_units: [{
amount: {
currency_code: "USD",
value: props.amount
}
}]
})
}}
/>
)
console.log response:
Order created successfully! Created order details: { product:
{ _id: '5e68330c8dcfa56868f1d23a',
name: 'Birdhouse',
description: 'A beautiful birdhouse',
price: 5,
quantity: 2 },
owner: { username: 'tgoandrex' },
createdAt: 2020-04-12T23:04:46.286Z,
_id: 5e93a16eb1cbc837d80167ef,
status: 'Created',
total: 10,
__v: 0 }
undefined
axios is expecting the endpoint to return a JSON object, but you're sending a string to the client:
res.status(200).json(`Order created successfully! Created order details: ${order}`);
Attempting to access res.data on the client will give you the string literal
"Order created successfully! Created order details: [elided]"
But because this is a string, the property _id does not exist on it, and so it will return undefined.
In order for this to work, you need to send just the object, rather than a string:
res.status(200).json(order);
Your /orders/new endpoint doesn't respond with JSON but with a simple string. Express's Response#json method is expecting an object that will be serialized to JSON, not a string.
You should just pass your order as single argument to the json method:
res.status(200).json(order);
otherwise, your React app won't be able to parse the JSON since it isn't a valid JSON format.

How can i assign computed properties to data with Vue.js?

In my CMS system, I can add pages. I want to also edit the existing content of these pages.
What I am trying to achieve is:
User edits page
Page updates in same firebase document where it was created
Page displays new content
Right now, I have set up getter and setter in computed where getter is getting data from the firebase collection and is providing the corresponding text I want to edit, and setter will commit to the changes I do with this text.
data: () => ({
title: null,
content: null
}),
computed: {
pages: {
get() {
return this.pageIndex
},
set() {
this.title
this.content
}
}
},
To update the document in firebase I am triggering this method:
methods: {
async update(id) {
return db
.collection('PAGES')
.doc(id)
.set({
title: this.title,
content: this.content
})
.then(() => {
return this.fetchPage()
})
},
}
But it posts this in my firebase document:
title: null
content: null
In the console, it shows undefined. When posting the data as with array or objects, it does post whatever changes I made. But that is my problem, I do not want to post it as an object or array, I am trying to post it as field names inside a document.
The current way I am doing it results in this:
What can I change to post the updated title and content?
Edit:
If I do it this way it will post the data but it will post as an object and an array:
data: function() {
return {
updatedPayload: {
title: null,
content: null
}
}
},
computed: {
pages: {
get() {
return this.pageIndex
},
set() {
this.updatedPayload = { title: this.title, content: this.content }
}
}
},
methods: {
async update(id) {
// return db.doc(`pages/${page.id}`)
const self = this
const pages = self.pages
return db
.collection('PAGES')
.doc(id)
.set({ pages })
.then(() => {
return this.fetchPage()
})
}
}
Here is the HTML
<tbody v-for="(page, idx) in pages" :key="page" class="">
<b-input v-model="page.title"></b-input>
<VueEditor v-model="page.content"></VueEditor>
</tbody>
So the dilemma is, I can only update data with an object, but I can not update data with field name, as it appears.
The OP of this thread is trying to achieve the same outcome as me, populate data for an input field, where as input shows existing information, and can populate new data. In my case, I need 2 inputs to populate new data, it already displays existing data from firebase.

Categories

Resources