AngularFire2 access data after retrieve it - javascript

I'm trying to get the data from my Firebase with AngularFire2.
I want to check specific data and after I get this data from Firebase, I can check it only in the specific scope and not after the operation to Firebase. Why does it happen?
Below is my code:
this.af.database.list('/users/1qfcMAQnglX9jsW5GdLpPko1HqE2', { preserveSnapshot: true})
.subscribe(snapshots=>{
snapshots.forEach(snapshot => {
if(snapshot.key=="reg_boolean"){
console.log(snapshot.val());
this.bo=snapshot.val();
}
this.currentUser.push({key:snapshot.key,value:snapshot.val()});
console.log(this.currentUser);
//console.log(snapshot.key, snapshot.val());
if(this.bo==true){console.log("happy"); }; //i can access only in this scope
});
})
if(this.bo==true){console.log("happy"); }; //why i can access this value??it's undefined, this happen before the subscribe with angularfire2

It's undefined because this.af.database.list it's asynchronous so the code in subscribe will execute when this.af.database.list does retrieve the data. So when the code got to the line if(this.bo==true){console.log("happy"); }; It has nothing because subscribe did not finish at all.
The subscribe it's like the old promise but now it's working with rxjs I recommend you to learn it because angular and ionic has a lot of focus on that.
Try looking at https://www.learnrxjs.io/

Related

Firebase response is too slow

Hey Guys I'm using Firebase Realtime Database to fetch some data for my React.js Web App.
There is a useState called Corr_User that should store the value of correct username.
I have a function to fetch the username from Firebase : -
function Login(){
const path = firebase.database().ref("users").child(username);
path.once("value")
.then(snapShot => {
setCorr_User(snapShot.child("username").val());
})
When I do this console.log(Corr_User) it prints a empty string indicating that the useState is not updated.
For confirmation, I also logged the snapshot.val(). This printed the appropriate value.
I could understand from this that Firebase is too slow in returning response hence my useState is not getting updated. Is my understanding correct? Is there any way to make my Login() function wait for the response?
Please Help !! Thanks in Advance.
EDIT -
Ok there was a bit of confusion I guess. The code goes likes this
function Login(){
....
.once("value")
.then(snapShot => { // After fetching the data
setCorr_User(snapShot.child("username").val()); // Assigning the value
console.log(snapShot.val()); // This should be logged first
}) // End of function snapShot()
// This is still inside Login()
console.log(Corr_User) // But this is logged first
} // End of Login()
It is from this, I was able to guess that Firebase is too slow in responding and useState is not getting updated accordingly as snapShot.val() is still empty.
Data is loaded from Firebase asynchronously, and the then is called when that data is available. What this means in practice is that your console.log(Corr_User) is called before console.log(snapShot.val()). For this reason, any code that needs data from the database must be inside the then callback, or be called from there.
This is an incredibly common stumbling block for developers new to asynchronous APIs, so I recommend studying some of these links:
Why Does Firebase Lose Reference outside the once() Function?
Best way to retrieve Firebase data and return it, or an alternative way
Firebase Query inside function returns null
Passing variable in parent scope to callback function

Asynchronous requests from angular

I have a method in service to get httprequest from another server and in the components console get null of value ollayer but data is not null
ollayer={};
service:
getLayer(): Observable<any> {
return this.http.get<olLayer>(
'http://localhost:3000/geomap/getlayer',
);
}
components:
this.olservice.getLayer().subscribe((data) => {
this.ollayer = data;
console.log(data)
});
console.log(this.ollayer)
That behaviour is fine, that's how asynchronous pipelines work.
For whatever reason you need the data, for example, to update a view on your web page, you have to do it in the subscribe scope. For example, you are updating this.ollayer inside the subscribe, so any view properly binded to that variable will update when the request is done.
Edit: it is ok for data being null on your second log because data may not have arrived yet. But it is not ok to be null on the log inside the subscribe.

Error loading uploaded API data into template

I have a service that returns me the user's data according to the Token stored in the localStorage and everything is returned correctly until I get to my component.
The real problem is that I have the following code in my component.ts file:
Apparently, at least everything should work out for me. However, in the console it is possible to notice that even after I have assigned the return to my user property it is printed as undefined. As shown in the image below.
When trying to use in the HTML template I get messages saying that it was not possible to load data from the user property. I have already tried using async pipe like this: (user $ | async) as user. I tried to use the safe navigation like this:user?.email.
But it did nothing and I have no idea why this happens. Any help will be welcome!
User$ represents a stream, should be assigned this way:
export class {
// a stream type
user$: Obserable<User>;
ngOnInit() {
// a stream type
this.user$ = this.clienteHeaderService.getUser();
}
}
and the template add |async pipe.
this.user is not immediately available after you call subscribe(), it is very possible that the getUser() hasn't emitted any result by the time console.log(this.user) is called.
If you just wanted to see what's in this.user, you may try it in the callback of subscribe()
this.clientHeaderService.getUser().subscribe((response) => {
this.user = response;
console.log(this.user); // this.user should be available here
})
On the template side, you should be able to just access the user via {{ user }}.
I'd also suggest to use share your minimum reproducible code at https://stackblitz.com/, to get help more easily.
Subscribe almost work like promise in javascript and it has a callback
.subscribe(response=>this.user=response)
Callback are pushed to the end of current event loop .So you are accessing
console.log(this.user)
before callback in your subscribe get executed.
instead
try
.subscribe((response) => {
this.user=response;
//you can access value of this.user here or call other function here to
//access this.user
console.log(this.user);
})

React + Firestore : Return a variable from a query

I'm learning React and Firestore currently and am a bit stuck. I'm trying to retrieve a users name from a firestore collection by searching their uid.
The following code is executed in a map of 'lessons' to create a list.
{lesson.post_author && findName(lesson.post_author)}
The following code is the findName function.
let findName = uid => {
firebase.firestore().collection("users")
.where('uid', '==', uid)
.get()
.then(querySnapshot => {
console.log(querySnapshot.docs[0].data().name);
});
};
Currently, the findName function will console log all of the names to the console successfully. I've altered the code to be able to console log outside of the firestore call, but that returns a promise pending in console.
The goal of the code is to return the name rather then the uid in the list.
Any help would be much appreciated.
Thank you!
As others have explained, you can't return that value, since it's loaded from Firestore asynchronously. By the time your return runs, the data hasn't loaded yet.
In React you handle this by putting the data in the component's state, and using it from there. If you do this, your render method can simply pick it up from the state, with something like:
{lesson.post_author && findName(lesson.post_author_name)}
(the above assumes that lesson indirectly comes from the state.
It's a bit easier if we pretend there's only one lesson, and you have these values straight in the state:
{state.post_author && findName(state.post_author_name)}
Now I'll assume you already have the post_author and you just need to look up the author's name. That means that somewhere in/after componentDidMount you'll load the additional data and add it to the state:
componentDidMount() {
firebase.firestore().collection("users")
.where('uid', '==', this.state.uid)
.get()
.then(querySnapshot => {
this.setState({ post_user_name: querySnapshot.docs[0].data().name });
});
}
Now the loading of the data still happens asynchronously, so the call to setState() happens some time after componentDidMount has completed. But React is aware that changing the state may require a refresh of the component, so it responds to the call to setState() by rerendering it.
Note that I'd highly recommend using each user's UID as the ID of the documents in users. That way you don't need a query and can just do a directly lookup:
componentDidMount() {
firebase.firestore().collection("users")
.doc(this.state.uid)
.get()
.then(doc => {
this.setState({ post_user_name: doc.data().name });
});
}
I'm trying to retrieve a users name from a firestore collection by
searching their uid.
This is accomplished by using the asyncronous .get method on a Firestore reference. In your case, you probably have a users collection of firebase.auth().currentUser.uid named documents.
var userRef = firebase.firestore().collection('users').doc(users.uid);
userRef.get().then(function(doc) {
if (doc.exists) {
console.log("Users first name is:", doc.data().firstName);
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
}).catch(function(error) {
console.log("Error getting document:", error);
});

return $add ref of Firebase item AngularFire

Let's say I have a collection of items:
var itemsRef = new Firebase("https://example.firebaseio.com/items");
$scope.items = $firebase(itemsRef);
And we $add an item:
$scope.items.$add($scope.item);
I understand the ref gets generated client side before making it to the Firebase collection.
How do I get that ref after adding the item? For example -Jx8363hdu12
AngularFire 0.6.0
As of AngularFire 0.6.0 $add, $save, $set and $remove use promises:
https://github.com/firebase/angularFire/issues/183
$scope.items.$add($scope.item).then(function(p){
console.log(p.name());
});
For others coming by here if you had code that previously worked using
var bleh = $scope.items.$add($scope.item);
Previously $add would return the actual value, but as Dan mentioned above it was changed to return a promise.
You have two options (the later being the better).
Move back to an older version of angularfire.
You will HAVE to use the .then operation.
I have had this problem a few times and keep running into it (i'm hoping the more variations on the term can be found the more likely if it happens again to me or others it'll be easier to find).
I had it in my head that a promise meant that you could still use the variable and at some point in the future it would fill it in (so I kept thinking that it would fill it in eventually and was getting annoyed that it never seemed to).
From here: http://www.html5rocks.com/en/tutorials/es6/promises/
Here's how you use that promise:
promise.then(function(result) {
console.log(result); // "Stuff worked!"
}, function(err) {
console.log(err); // Error: "It broke"
});
"then" takes two arguments, a callback for a success case, and another for the failure case. Both are optional, so you can add a callback for the success or failure case only.
So in theory if firebase errors out saving the data (adding/removing/etc) you should at least have the error function so you can trap that something wrong has happened.
For Dan's example:
$scope.items.$add($scope.item).then(
function(p){
console.log(p.name());
},
function(err){
console.log("The expected Firebase action failed to occur this was your error: " + err);
});

Categories