Firebase: snap.child(...).once is not a function - javascript

I want to listen to some data from my firebase but I first have to check if the keys leading to the specific data has already been made. If they have not yet been made there is no problem I don't have to listen.
I get this error message:
snip.child(...).once is not a function
I have tried to just followed their documentation from here it really seems as if what I am doing should be legal.
The .hasChild(...) works fine.
rootRef.child('match-feed-comments').once('value', snip => {
if (snip.hasChild(this.props.matchId)) { // Checking if child exists
snip.child(this.props.matchId).once('value', snup => { // if the child exists we listen
if (snup.hasChild(this.props.feedComponentId)) {
snup.child(this.props.feedComponentId).on('value', snap => {
// finally I want to do stuff with the data
this.setState({
comments: snap.val()
})
});
}
});
}
});

Related

How to perform two firebase database operation on different nodes in single request/function?

I am a react-native developer and new to firebase. I am performing firebase realtime database operation, have a look at code below;
firebase.database().ref('events/wedding/items').push(object).then((data) => {
//success callback
dispatch(addPendingInvoice({ ...invoice, id: data.key }))
Alert.alert('Successfully added to Invoices', 'Please go to invoice section to clear first and continue.', [{ text: 'Ok' }])
}).catch((error) => {
//error callback
Alert.alert("Can't book package.", 'Please check your internet connection!', [{ text: 'OK', style: 'destructive' }])
})
Now, I wish to push another object to another node events/wedding/packages right after this firebase database function above. I can use another function inside then callback in above firebase functions. This is not a professional way to do this.
Is there any way to do this?
You can use the update() method to "simultaneously write to specific children of a node without overwriting other child nodes". Note that "simultaneous updates made this way are atomic: either all updates succeed or all updates fails", see the doc.
So in your case you would do along the following lines:
var newNodeKey = firebase.database().ref().child('events/wedding/items').push().key;
var updates = {};
updates['events/wedding/items/' + newNodeKey] = { foo: "bar" };
updates['events/wedding/packages/' + newNodeKey] = { bar: "foo" };
firebase.database().ref().update(updates)
.then(() => {
// The two writes are completed, do whatever you need
// e.g. dispatch(...);
});
All Firebase operations return a promise so you can use Promise.all() to run them all simultaneously.
Promise.all([
firebase.database().ref(reference).set({}),
firebase.database().ref(reference2).set({})
]).then(() => {
console.log("Operations Successful")
}).catch((e) => console.log(e))
You can also push all your operations to an array and then pass that array in Promise.all()

Emit an event, wait, and then emit another one in Vue

I have the following events that emit to the parent component and work fine:
this.$emit('sendToParent1', true);
this.$emit('sendToParent2');
this.$emit('sendToParent3');
this.$emit('sendToParent4', true);
this.$emit('sendToParent5', false);
However, I ran into a specific problem which requires me to do emit those 5 events, in that order, but one after another.
//emit this first and wait until the parent has finished what its supposed to do
this.$emit('sendToParent1');
//after the parent is done, we emit the next one from the child
this.$emit('sendToParent2');
//same behavior, once it is emitted to parent and the parent finishes executing, then we emit the next one...
this.$emit('sendToParent3');
//again
this.$emit('sendToParent4', true);
//and again
this.$emit('sendToParent5', false);
I believe that the best approach is to have a prop detecting when an event has finished executing whatever it is supposed to do and then sending a prop to the child, then when that prop is updated the next emit gets executed and so forth and so on. Can someone point me in the best direction of how to solve this?
The Parent has the following component:
<component v-on:sendToParent5="parentFunctionFive" v-on:sendToParent4="parentFunctionFour" v-on:sendToParent1="parentFunctionOne" v-on:sendToParent2="parentFunctionTwo" v-on:sendToParent3="parentFunctionThree"></component>
The Parent has the following methods:
parentFunctionOne: function() {
axios.get('/api/do_something-1/')
.then((res)=>{
this.something1 = res.data;
})
.catch((error)=>{
console.log(error);
});
},
parentFunctionTwo: function() {
axios.get('/api/do_something-2/')
.then((res)=>{
this.something2 = res.data;
})
.catch((error)=>{
console.log(error);
});
},
parentFunctionThree: function() {
axios.get('/api/do_something-3/')
.then((res)=>{
this.something3 = res.data;
})
.catch((error)=>{
console.log(error);
});
},
parentFunctionFour: function(passed_value) {
//changing the value of a variable in data
this.trueOrFalse = passed_value;
},
parentFunctionFive: function(passed_value) {
//changing the value of a variable in data
this.valuePassed = passed_value;
}
It would easier for us to answer if you explained what your ultimate goal is (see XY problem), but from the information that you provided I think proper way would be to pass a function called next as the event param and trigger it when the work is done which will make the process to continue to the next stage.
this.$emit('sendToParent1', {
value: true,
next:
() => this.$emit('sendToParent2', {
value: false,
next: () => this.$emit('sendToParent3', false);
})
});
parentFunctionOne: function({value, next}) {
axios.get('/api/do_something-1/')
.then((res)=>{
this.something1 = res.data;
next() // proceed
})
.catch((error)=>{
console.log(error);
});
},
parentFunctionTwo: function({value, next}) {
axios.get('/api/do_something-2/')
.then((res)=>{
this.something2 = res.data;
next() // proceed
})
.catch((error)=>{
console.log(error);
});
},
parentFunctionThree: function() {
axios.get('/api/do_something-3/')
.then((res)=>{
this.something3 = res.data;
})
.catch((error)=>{
console.log(error);
});
},
you should rethink about the way you implement the component i think you could try emit one event with all the information you need and in the parent call the appropriate function.
if you want to implement it in this way you could us a variable that you send with event and increment it every time an event has occurred and in the parent track that variable and execute the event in the right order like in the http protocole

Trigger won't return the proper user object in Realm/MongoDB

With Realm sync of MongoDB, I'm trying to launch a trigger when a realm user is created to insert his newly created ID into my cluster. Here's the javascript function I made that is being called by the trigger :
exports = async function createNewUserDocument({ user }) {
const users = context.services
.get("mongodb-atlas")
.db("BD")
.collection("patients");
const query = { email: context.user.data.email };
const update = {
$set: {
patientId: context.user.id
}
};
// Return the updated document instead of the original document
const options = { returnNewDocument: true };
console.log(context.user.data.email);
return users.findOneAndUpdate(query, update, options)
.then(updatedDocument => {
if(updatedDocument) {
console.log(`Successfully updated document: ${updatedDocument}.`)
} else {
console.log("No document matches the provided query.")
}
return updatedDocument
})
.catch(err => console.error(`Failed to find and update document: ${err}`))
};
When running from the embed editor, while specifying the proper user manually, it's working perfectly. However, when launched by the trigger, it looks like the user is the system user and not the created user, because the error I get in the logs is the same I get when I run from the editor by specifying System user, which is Failed to find and update document: FunctionError: cannot compare to undefined. This makes sense because the System user is not a user per se, so the context.user is undefined.
I find it weird since I specify in the function settings that it should be executed with the permissions of the user calling the function. So my question is, is it possible to access the user.context of a user on his creation, and if so, how would I do it ?

Returning a record with a string in ember

I am trying to implement a search function where a user can return other users by passing a username through a component. I followed the ember guides and have the following code to do so in my routes file:
import Ember from 'ember';
export default Ember.Route.extend({
flashMessages: Ember.inject.service(),
actions: {
searchAccount (params) {
// let accounts = this.get('store').peekAll('account');
// let account = accounts.filterBy('user_name', params.userName);
// console.log(account);
this.get('store').peekAll('account')
.then((accounts) => {
return accounts.filterBy('user_name', params.userName);
})
.then((account) => {
console.log(account);
this.get('flashMessages')
.success('account retrieved');
})
.catch(() => {
this.get('flashMessages')
.danger('There was a problem. Please try again.');
});
}
}
});
This code, however, throws me the following error:
"You cannot pass '[object Object]' as id to the store's find method"
I think that this implementation of the .find method is no longer valid, and I need to go about returning the object in a different manner. How would I go about doing this?
You can't do .then for filterBy.
You can't do .then for peekAll. because both will not return the Promise.
Calling asynchronous code and inside the searchAccount and returning the result doesn't make much sense here. since searchAccount will return quickly before completion of async code.
this.get('store').findAll('account',{reload:true}).then((accounts) =>{
if(accounts.findBy('user_name', params.userName)){
// show exists message
} else {
//show does not exist message
}
});
the above code will contact the server, and get all the result and then do findBy for the filtering. so filtering is done in client side. instead of this you can do query,
this.store.query('account', { filter: { user_name: params.userName } }).then(accounts =>{
//you can check with length accounts.length>0
//or you accounts.get('firstObject').get('user_name') === params.userName
//show success message appropriately.
});
DS.Store#find is not a valid method in modern versions of Ember Data. If the users are already in the store, you can peek and filter them:
this.store.peekAll('account').filterBy('user_name', params.userName);
Otherwise, you'll need to use the same approach you used in your earlier question, and query them (assuming your backend supports filtering):
this.store.query('account', { filter: { user_name: params.userName } });

Firebase React Binding

I'm somewhat new to React, and using the re-base library to work with Firebase.
I'm currently trying to render a table, but because of the way my data is structured in firebase, I need to get a list of keys from two locations- the first one being a list of user keys that are a member of a team, and the second being the full user information.
The team node is structured like this: /teams/team_id/userkeys, and the user info is stored like this: /Users/userkey/{email, name, etc.}
My table consists of two react components: a table component and a row component.
My table component has props teamid passed to it, and I'm using re-base's bindToState functionality to get the associated user keys in componentWillMount(). Then, I use bindToState again to get the full user node, like so:
componentWillMount() {
this.ref = base.bindToState(`/teams/${this.props.data}/members`, {
context: this,
state: 'members',
asArray: true,
then() {
this.secondref = base.bindToState('/Users', {
context: this,
state: 'users',
asArray: true,
then() {
let membersKeys = this.state.members.map(function(item) {
return item.key;
});
let usersKeys = this.state.members.map(function(item) {
return item.key;
});
let onlyCorrectMembersKeys = intersection(membersKeys, usersKeys);
this.setState({
loading: false
});
}
});
}
});
}
As you can see, I create membersKeys and usersKeys and then use underscore.js's intersection function to get all the member keys that are in my users node (note: I do this because there are some cases where a user will be a member of a team, but not be under /Users).
The part I'm struggling with is adding an additional rebase call to create the full members array (ie. the user data from /Users for the keys in onlyCorrectMembersKeys.
Edit: I've tried
let allKeys = [];
onlyCorrectMembersKeys.forEach(function(element) {
base.fetch(`/Users/${element}`, {
asArray: true,
then(data) {
allKeys.prototype.concat(data);
}
});
});
But I'm receiving the error Error: REBASE: The options argument must contain a context property of type object. Instead, got undefined
I'm assuming that's because onlyCorrectMembersKeys hasn't been fully computed yet, but I'm struggling with how to figure out the best way to solve this..
For anyone dealing with this issue as well, I seemed to have found (somewhat) of a solution:
onlyCorrectMembersKeys.map(function(item) {
base.fetch(`/Users/${item}`, {
context: this,
asObject: true,
then(data) {
if (data) {
allKeyss.push({item,data});
this.setState({allKeys: allKeyss});
}
this.setState({loading: false});
},
onFailure(err) {
console.log(err);
this.setState({loading: false});
}
})
}, this);
}
This works fine, but when users and members state is updated, it doesn't update the allkeys state. I'm sure this is just due to my level of react knowledge, so when I figure that out I'll post the solution.
Edit: using listenTo instead of bindToState is the correct approach as bindToState's callback is only fired once.

Categories