Uploading an Array to Firestore - javascript

I am trying to upload an array from my application to Firestone. However, it continues to show
"firebaseError: function fieldvalue.arrayUnion()" called with invalid data.
Here is my code:
const finalList = this.state.players.map(player => {
if(player.Present === true){
return player.id
}
})
finalList.map( id =>
dbh.collection("Groups").doc(this.state.group)
.collection('Enrolled').doc('ids').update({
players: firebase.firestore.FieldValue.arrayUnion(id)
})
)
Note that the array "players" does not exist in the database yet.

You can also use the set with merge approach that i have discussed on comments in question
Other than that you can use
const finalList = this.state.players.map(player => {
if(player.Present === true){
return player.id
}
})
dbh.collection("Groups").doc(this.state.group)
.collection('Enrolled').doc('ids').update({
players: firebase.firestore.FieldValue.arrayUnion(...finalList)
})
you can get the detailed info from answer by Matthew Rideout

It seems like your id variable is an array of IDs, while the arrayUnion function expects a repeatable variable.
To convert the type, you can use the ... operation:
finalList.map( id =>
dbh.collection("Groups").doc(this.state.group).collection('Enrolled').doc('ids').update({
players: firebase.firestore.FieldValue.arrayUnion(...id)
})
)
Alternatively, and more universalle, you can can perform the conversion with this:
firebase.firestore.FieldValue.arrayUnion.apply(null, id);
For more on this, see Gil's answer in this post to the firebase-talk mailing list.

Related

How to integrate this filter into my code?

I have some list of names that I take from the array using the Fetch method. Now I'm using the method of searchHandler at the click of a button, I enter the input data into the console:
https://codesandbox.io/s/jovial-lovelace-z659k
But I need to enter the input "First name", and click on the button, only a line with that name was displayed. But I don't know how to make the filter myself.
I found the solution on the internet, but unfortunately I can't integrate it into my code.Here it is:
getFilteredData() {
if (!this.state.search){
return this.state.data
}
return this.state.data.filter(item=>{
return item["firstName"].toLowerCase().includes(this.state.search.toLowerCase())
});
}
How to integrate it into my code? And what to write in the render method?
You are in the right direction there. The correct code (with comments explaining the changes) should be:
searchHandler = search => {
// This if checks if search is empty. In that case, it reset the data to print the initial list again
if (search) {
// This 'arr' variable is a copy of what you do in your Table.js
const arr = this.state.data.group && this.state.data.group.first ? this.state.data.group.first : this.state.data;
const filteredArr = arr.filter((item) => {
// Here you compare with 'search' instead of 'state.search', since you didn't updated the state to include the search term
return item["firstName"].toLowerCase().includes(search.toLowerCase())
})
// This will update your state, which also updates the table
this.setState({data: filteredArr})
} else {
// As explained, if search was empty, return everything
this.resetData();
}
};
// This is a copy of what you have in componentDidMount
async resetData() {
const response = await fetch("/data/mass.json");
const data = await response.json();
this.setState({
data
});
}
Note:
includes is not supported by all browsers, as you can see here. If you need a more reliable solution, you could use indexOf, as explained here.
Since your fetched data is an array of objects, and you basically want to filter out the objects which match the serach criteria, heres how you can write your search handler:
searchHandler = search => {
const { data } = this.state;
const filteredData = {
group: {
first: Object.values(data.group.first).filter(item => item.firstName.includes(search)),
}
}
this.setState({ data: filteredData });
};
Basically, what its doing is taking the array of objects out of dataand filter out only those objects which have the name you search for. and sets the filtered array of objects in the same structure as your original data object is and there you go!!
Also you don't have to make any changes to the render method now. Since render method is already working with the state which has data in it. and as soon as you make a search state, data will be updated and available in the render.

how to get items from array on respectively

I have an array contain unique keys, these keys I want to take it to get his Data from firebase real-time DB
the array like this
["dtnMG3dVEQXL1TPct3awl927jax2", "y5eAQppoEZRELsDL3hLbY5b0A763"]
so I iterate it using forEach Method to get a single key to check if found in the users tree or not But the result is null?
But when I Log the keys.forEach(key => console.log(key)) I can Get every key in a single line
firebase
.database()
.ref("users")
.child(keys.forEach(key => key))
.once("value")
.then(users => {
// let username = users.val().username;
console.log(users.val());
// Object.assign(users, { [Object.keys(users)]: username });
});
Edit
So thats fine and get every single username, Now the new issues is i want to save these names to other object i have
users = {
dtnMG3dVEQXL1TPct3awl927jax2: // the unique key
-LmUSeyWtBPqcoN8l6fd: [{…}]
-LmUgcbdzElxWpLkN-F9: [{…}]
// here's i want to add somthing like thies
-username : "userOne" //I got it from the Looping
y5eAQppoEZRELsDL3hLbY5b0A763:
-LmSTYlxa7bjy0Beazmy: [{…}]
-LmUad3lvkPdTQDFo4Ds: [{…}]
-LmUyDmGcEmYKOJnvEiA: [{…}]
// here's i want to add somthing like thies
-username : "userTwo" // I got it from the Looping
}
keys.forEach(key => key)
This return every element of the array keys. And you are passing these elements to the child function. However, child functions expects only one argument.
Try this:
for( let key of keys){
firebase
.database()
.ref("users")
.child(key)
.once("value")
.then(users => {
// let username = users.val().username;
console.log(users.val());
// Object.assign(users, { [Object.keys(users)]: username });
});
}
Side not: Do not call async function inside forEach loop.
.child(keys.forEach(key => key)) - This is your problem. This forEach effectively returns all your array values one by one. But you call it inside a function with one argument that gets called one time. So it will only check for the first value in your array and the rest will be dumped, so its like looking inside a 1-element array.
Solution is to perform the DB calls inside the iteration:
keys.forEach( key => {
firebase
.database()
.ref("users")
.child(key)
.once("value")
.then(users => {
// let username = users.val().username;
console.log(users.val());
// Object.assign(users, { [Object.keys(users)]: username });
}});
EDIT:
to do what you wanted after you edited, assuming the variable curUser holds the username you want to insert and curKey is the key associated to that username, just: users[key]["username"] = curUser;. This will reference the property which is named after curKey in the users object, and will create a "username" property in it and store the value there.
Notice that if you already have "username" property in this key object it will be overriden.
** I assumed the data structure of the key-properties inside "users" are objects even tho your code doen't really show it.

Can I treat items found through a Promise.all as a firebase collection?

I am stuck in what I thought was a very simple use case: I have a list of client ids in an array. All I want to do is fetch all those clients and "watch" them (using the .onSnapshot).
To fetch the client objects, it is nice and simple, I simply go through the array and get each client by their id. The code looks something like this:
const accessibleClients = ['client1', 'client2', 'client3']
const clients = await Promise.all(
accessibleClients.map(async clientId => {
return db
.collection('clients')
.doc(clientId)
.get()
})
)
If I just needed the list of clients, it would be fine, but I need to perform the .onSnapshot on it to see changes of the clients I am displaying. Is this possible to do? How can I get around this issue?
I am working with AngularFire so it is a bit different. But i also had the problem that i need to listen to unrelated documents which can not be queried.
I solved this with an object which contains all the snapshot listeners. This allows you to unsubscribe from individual client snapshots or from all snapshot if you do not need it anymore.
const accessibleClients = ['client1', 'client2', 'client3'];
const clientSnapshotObject = {};
const clientDataArray = [];
accessibleClients.forEach(clientId => {
clientSnapshotArray[clientId] = {
db.collection('clients').doc(clientId).onSnapshot(doc => {
const client = clientDataArray.find(client => doc.id === client.clientId);
if (client) {
const index = clientDataArray.findIndex(client => doc.id === client.clientId);
clientDataArray.splice(index, 1 , doc.data())
} else {
clientDataArray.push(doc.data());
}
})
};
})
With the clientIds of the accessibleClients array, i create an object of DocumentSnapshots with the clientId as property key.
The snapshot callback function pushes the specific client data into the clientDataArray. If a snapshot changes the callback function replaces the old data with the new data.
I do not know your exact data model but i hope this code helps with your problem.

How to create a `context.Provider`/`context.Consumer`-like structure to pass values in a bot app?

I'm trying to pass a property, that is inside the first position of an array of objects, to another module so I can use this value later. I've tried to pass it as module(args), but it keeps reading the default value which is 0. Is there a way to do this?
I tried to implement some React.context but the Bot framework Emulator is refusing it.
/////////////////Module that ll acquire the value/////////////////////////////
getCard(bot, builder, params) {
let configValues = { ...params[0] }
bot.dialog(`${configValues.path}`, function (session) {
var msg = new builder.Message(session);
const cardItem = (obj) => {
return (new builder.HeroCard(session)
.title(`${obj.title}`)
.text(`R$ ${obj.price}`)
.images([builder.CardImage.create(session, `${obj.img}`)])
.buttons([
builder.CardAction.imBack(session, `${obj.price} Item adicionado!`, 'add to cart')
// !onClick event must add the current obj.price to
// the configValues.total(Ex: configValues.total += obj.price)!
])
)
}
msg.attachmentLayout(builder.AttachmentLayout.carousel)
msg.attachments(
eval(params.map(obj => cardItem(obj)))
);
//!in here before end the dialog is where i want to update
// the configValues.total so i can show it in the -> Checkout module
session.send(msg).endDialog()
}).triggerAction({ matches: configValues.regex });
}
}
//////////////CheckOut.Module///////////////////////////////
{...}
let configValues = { ...params[0] }
let state = {
nome: "",
endereco: "",
pagamento: "",
total: configValues.total // this is the value to be read
}
bot.dialog('/intent', [
{...},
(session, results) => {
state.pagamento = results.response
session.send(
JSON.stringify(state) // here is the place to be printed
)
{...}
]
).triggerAction({ matches: /^(finalizar|checar|encerrar|confirmar pedido|terminar)/i })
Since you solved your original problem, I'll answer the one in your comment.
Your problem is here:
cartId.map((obj, i , arr) => {
// if (!obj.total) {
// obj.total.reduce(i => i += i)
// }
const newtotal = new total
newtotal.getTotals(bot, builder, obj, arr)
})
cartId contains the totals for each of your items. When you call map on it, you're passing each item individually to getTotals, which passes each item to checkout()
The reason you can't sum all of the totals and can only sum one item's total is that you pass cartId to checkout and cartId has been changed to just a single item. Instead, there's a couple of different things you could do:
Pass the whole cartId from cartItems and use something like for (var key in cartItems) in totalConstructor() and checkoutConstructor(). This is probably the easiest, but not very memory efficient.
Use BotBuilder's State Storage to store your totals array in userData, then sum that at the end. This might be more difficult to implement, but would be a much better route to go. Here's a sample that can help you get started.

Get the ChildrenCount of a child in Firebase using JavaScript

I have been doing this for an hour. I simply want to get the number of children in the child "Success" in the database below. The answers in similar stackoverflow questions are not working. I am new in Javascript Programming.
So far I have tried this
var children = firebase.database().ref('Success/').onWrite(event => {
return event.data.ref.parent.once("value", (snapshot) => {
const count = snapshot.numChildren();
console.log(count);
})
})
and also this
var children = firebase.database().ref('Success/').onWrite(event => {
return event.data.ref.parent.once("value", (snapshot) => {
const count = snapshot.numChildren();
console.log(count);
})
})
Where might I be going wrong.
As explained in the doc, you have to use the numChildren() method, as follows:
var ref = firebase.database().ref("Success");
ref.once("value")
.then(function(snapshot) {
console.log(snapshot.numChildren());
});
If you want to use this method in a Cloud Function, you can do as follows:
exports.children = functions.database
.ref('/Success')
.onWrite((change, context) => {
console.log(change.after.numChildren());
return null;
});
Note that:
The new syntax for Cloud Functions version > 1.0 is used, see https://firebase.google.com/docs/functions/beta-v1-diff?authuser=0
You should not forget to return a promise or a value to indicate to the platform that the Cloud Function execution is completed (for more details on this point, you may watch the 3 videos about "JavaScript Promises" from the Firebase video series: https://firebase.google.com/docs/functions/video-series/).
const db = getDatabase(app)
const questionsRef = ref(db, 'questions')
const mathematicalLiteracy = child(questionsRef, 'mathematicalLiteracy')
onValue(mathematicalLiteracy, (snapshot) => {
const data = snapshot.val()
const lenML = data.length - 1
console.log(lenML)
})
This method worked for me. I wanted to get the children's count of the mathematicalLiteracy node in my database tree. If I get its value using .val() it returns an array that contains that node's children and an extra empty item. So, I subtracted that one empty item's count. Finally, I get my needed children's count.

Categories

Resources