asynchronous callback returns null - javascript

I am trying to assign callback function values and display ng-repeat
var myApp = angular.module('mybet',[]);
myApp.controller('MyBetController', function($scope,$firebase,ProfileFactory,MybetFactory){
$scope.myBetEvents = MybetFactory.getUsersBetsProfile(currentLoggedUser, function(betsData){
//Callback retuning null value here
$scope.myBetEvents = betsData;
});
});
myApp.factory('MybetFactory', ['$firebase', function($firebase){
var factory = {};
factory.getUsersBetsProfile = function(userId, callback){
var firebaseRef = new Firebase("https://xxx.firebaseio.com/usersbetsprofile").child(userId);
var firebaseBetsRef = new Firebase("https://xxx.firebaseio.com/events");
var userBettedEvent = [];
//retrive the data
firebaseRef.on('value', function (snapshot) {
console.log(snapshot.val());
var data = snapshot.val();
if (data){
//callback(data);
angular.forEach(data, function(data){
console.log('For Each getUsersBets',data);
var firebaseEventsData = firebaseBetsRef.child(data.event_id);
//retrive the data of bets
firebaseEventsData.on('value', function (snapshot) {
userBettedEvent.push(snapshot.val());
console.log('userBettedEvent',userBettedEvent);
//if I call callback here then i get the values but calling callback multipul time is no the option
}, function (errorObject) {
console.log('The read failed: ' + errorObject.code);
});
});
//ISSUE:: callback returing null values
callback(userBettedEvent);
}else{
console.log('Users has no bets');
}
}, function (errorObject) {
console.log('The read failed: ' + errorObject.code);
});
};
return factory;
}])
View::
<ul class="list">
<li class="item" ng-repeat="events in myBetEvents">{{events.event_name}}</li>
</ul>
How can get callback() to return values and display in view? what is causing callback after foreach returning null?

As #coder already said in the comments, your callback(userBettedEvent) is in the wrong place. Where you have it now, it will be invoked before the on('value' from Firebase has completed.
From the comments it is clear that you've notice that, yet only want the callback to invoked for one child. You can simply use a limit for that.
Something like this would work:
if (data){
data.limit(1).on('value', function(childData) {
var firebaseEventsData = firebaseBetsRef.child(childData.event_id);
firebaseEventsData.on('value', function (snapshot) {
userBettedEvent.push(snapshot.val());
callback(userBettedEvent);
}
})
I haven't really tested the code, but I'm quite sure a limit should get you in the right direction.
As a side note: you seem to be using Firebase as a traditional database, pulling the data out like you'd do with SQL. As you may notice: that is not a natural model for Firebase, which was made to synchronize data changes. If you want to stick to pulling the data, you may consider using once('value' instead of on('value' to ensure the handlers fire only once for every time you try to pull the data from Firebase.

Related

promises only working properly with then function

I have a button which executes a function with a promise that gets and displays data from firebase in html (I'm using angularJS, ionic and firebase).
The problem is : if I don't inlclude a .then(function(){}) after it, the promise gets executed in unsynchronous way, meaning I have to click the button once again so the data gets displayed in html.
I want to put the data in the scope after the promise (that gets the data from firebase), but for some reason in only works if I put a .then function after it.
However, the data gets displayed normally in the console, but not in html (meaning I think that the function doesn't get attached to the scope).
Here is the piece of code :
$scope.displayChat = function () {
var userId = firebase.auth().currentUser.uid; // Get ID
var deferred = $q.defer()
var db = firebase.database();
var ref = db.ref("12346787");
ref.on("value", function(snapshot) {
console.log(snapshot.val());
$scope.chatArray = snapshot.val();
deferred.resolve()
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
})
return deferred.promise.then(function(){
// Removing this empty .then(function(){}) function
// will result in asynchronousity.
// just "return deferred.promise;" doesn't work.
})
}
Any solutions? I don't have much experience with promises but I didn't find anything related. Cheers.
the purpose of promise is to manage asynchronous methods, so I don't really understand the problem...
Moreover normally the code inside displayChat must be executed, only the callback must be executed after. You should return the promise, that enable you to execute callback once you are sure the required asynchronous method are done.
When changes to scope are done by events external to the AngularJS framework, the framework needs to do an $apply to initiate a digest cycle to update the DOM.
(source: angularjs.org)
The .then method of a $q service promise automatically initiates the necessary digest cycle. In this case, the promise returned by the displayChat function is discarded and no digest cycle gets initiated. The subsequent click of the button initiates the digest cycle.
In the future, someone might wish to chain from the promise returned by the displayChat function. I recommend making the function more versatile by returning a proper promise and moving any changes to scope into a .then method.
$scope.displayChat = function () {
var userId = firebase.auth().currentUser.uid; // Get ID
var deferred = $q.defer()
var db = firebase.database();
var ref = db.ref("12346787");
//ref.on("value", function(snapshot) {
//USE once
ref.once("value", function(snapshot) {
console.log(snapshot.val());
//$scope.chatArray = snapshot.val();
deferred.resolve(snapshot.val());
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
//ADD rejection case
deferred.reject(errorObject);
})
return deferred.promise.then(function(chatArray){
//Move scope changes here
$scope.chatArray = chatArray;
//RETURN to chain data
return chatArray;
// Removing this empty .then(function(){}) function
// will result in asynchronousity.
// just "return deferred.promise;" doesn't work.
})
}
Also to avoid memory leaks, use ref.once instead of ref.on and be sure to reject the promise in the error case.
Promises are used to defer execution of some logic until the promise has been satisfied - ie: you have received your results from the database. In your case, you are already deferring your console display and setting of the $scope variable within ref.on. The promise code is redundant.
The fact that your result shows in the console proves you have received the result. When you update data in the scope, it will not appear until a digest cycle has occurred. Most of the time, Angular can automatically figure out when it needs to run a digest cycle. On those times when it does not, you can force it by wrapping your scope related logic in a timeout, in which case, your code would look as follows:
$scope.displayChat = function () {
var userId = firebase.auth().currentUser.uid; // Get ID
var db = firebase.database();
var ref = db.ref("12346787");
ref.on("value", function(snapshot) {
console.log(snapshot.val());
$timeout(function () {
$scope.chatArray = snapshot.val();
});
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
})
}
Your use of the promise .then method just happened to trigger a digest cycle. The promise itself really wasn't doing anything.
If your intention was to pass the snapshot back to the caller when it became available, that's when the promise comes into play, and would be done as follows:
$scope.displayChat = function () {
var userId = firebase.auth().currentUser.uid; // Get ID
var deferred = $q.defer()
var db = firebase.database();
var ref = db.ref("12346787");
ref.on("value", function(snapshot) {
deferred.resolve(snapshot)
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
})
return deferred.promise;
};
$scope.callDisplayChat = function () {
$scope.displayChat().then(function (result) {
$scope.chatArray = result.val();
});
};

Get values from function Woocommerce?

I need to get values from function Woocommerce. So I want the values display out of the function. Is there another simple solution to solve it?
WooCommerce.get('products?per_page=100', function(err, data, res) {
var data = JSON.parse(res);
var id = data[0]['id'];
});
console.log(id); // output is undefined
get is async, meaning that its callback is called after everything has finished.
id is not available anymore as it's scoped to the get callback function.
To help you debug, you could try:
var id;
WooCommerce.get('products?per_page=100', function(err, data, res) {
var data = JSON.parse(res);
// keep the data you want in another variable, or object.
id = data[0]['id'];
console.log(id); // test inside the function
});
The callback gets called when a response is received from the server.

Javascript function doesn't return query result

I am trying to figure out why one of my queries won't return the value from a query...my code looks like this:
var client = new pg.Client(conString);
client.connect();
var query = client.query("SELECT count(*) as count FROM sat_scores")
// Don't use demo key in production. Get a key from https://api.nasa.gov/index.html#apply-for-an-api-key
function getNEO(callback) {
var data = '';
query.on('rows', function(rows) {
console.log("Row count is: %s", rows[0].count)
data += rows[0].count;
});
query.on('end', function() {
callback(data);
});
}
with that, getNEO returns a blank...but if I set var data = '4', then getNEO returns 4....the query should return 128 but it just returns a blank...
First of all, getNEO() doesn't return anything - I'm operating on the assumption that you call getNEO() exactly once for your query, and pass in a callback to handle the data, and that callback is what's not getting the appropriate data?
My typical recommendation for troubleshooting things like this is to simplify your code, and try and get really close to any example code given (for instance):
var client = new pg.Client(conString);
// define your callback here, in theory
client.connect(function (err) {
if (err) throw err;
var query = client.query("SELECT count(*) as count FROM sat_scores"),
function(err, result) {
if (err) throw err;
console.log(result.rows.length);
}
);
});
... I'm doing a couple things here you'll want to note:
It looks like the client.connect() method is asynchronous - you can't just connect and then go run your query, you have to wait until the connection is completed, hence the callback. Looking through the code, it looks like it may emit a connect event when it's ready to send queries, so you don't have to use a callback on the connect() method directly.
I don't see a data event in the documentation for the query object nor do I see one in the code. You could use the row event, or you could use a callback directly on the query as in the example on the main page - that's what I've done here in the interest of simplicity.
I don't see the count property you're using, and row[0] is only going to be the first result - I think you want the length property on the whole rows array if you're looking for the number of rows returned.
I don't know if you have a good reason to use the getNEO() function as opposed to putting the code directly in procedurally, but I think you can get a closer approximation of what you're after like this:
var client = new pg.Client(conString);
// define your callback here, in theory
client.connect();
function getNEO(callback) {
client.on('connect', function () {
var query = client.query("SELECT count(*) as count FROM sat_scores"));
query.on('end', function(result) {
callback(result.rowCount);
});
});
}
... so, you can call your getNEO() function whenever you like, it'll appropriately wait for the connection to be completed, and then you can skip tracking each row as it comes; the end event receives the result object which will give you all the rows and the row count to do with what you wish.
so here is how I was able to resolve the issue....I moved the var query inside of the function
function getNEO(state, callback) {
var conString = "postgres://alexa:al#alexadb2.cgh3p2.us-east-1.redshift.amazonaws.com:5439/alexa";
var client = new pg.Client(conString);
client.connect();
var data = '';
var query = client.query("SELECT avg(Math) as math, avg(Reading) as reading FROM sat_scores WHERE State = '" + state + "'");
console.log("query is: %s", query);
query.on('row', function(row) {
console.log("Row cnt is: %s", row.math);
console.log("row is: " + row)
data += row;
});
console.log("made it");
query.on('end', function() {
callback(data);
});
}

How to make synchronous call in Angular JS?

The following code supposed to be update username in the data base then retrieve updated username.
updateUserMame and getUserName are two different REST calls.
updateName(name) {
var obj = this;
if (name === 'None') {
name = null;
}
obj.UtilityService.updateUserName(name, obj.userId)
.success(function (data) {
if (data) {
obj.getUserName(obj.userId);
console.log('Name is updated for ID:'||obj.userId);
} else {
console.log('Something Wrong');
}
});
}
getUserName(userId){
obj.UtilityService.getUserName(userId)
.then(function (result) {
console.log(result.user.userId);
}
}
I have user name 'Nathan Drake' in the dataBase.
When I run the update function with 'Elena Fisher', it is returning 'Nathan Drake'.
I've read some articles to make synchronus service calls, but unable to figure out what is going wrong.
Please help.
You could wrap your update function in a promise:
var updatePromise = $q.when(updateName(name)); // creates a promise
When your promise has finished processing, you can resolve it using then() which takes a success callback and an error callback
updatePromise().then(function successCallback(response){ // resolves the promise using then
getUserName(userId) // execute the rest of your code
},
function errorCallback(response){
console.log(error)
});
You would need to inject $q into the scope you are working with
Your code does not make much sense, that is I see possible mistakes as it looks like you are interchanging user name and user id and calling the obj context from inside a function even when its not declared there etc. Either we are missing code or this will fail when you try to run it.
Here is your example with some fixes and comments that show how you could do it using callbacks (no sync code, as mentioned by everyone else on this thread you should avoid actually waiting for I/O and use callbacks instead).
updateName(name) {
var obj = this; // good, you captured this
if (name === 'None') {
name = null;
}
obj.UtilityService.updateUserName(name, obj.userId)
.success(function (data) {
if (data) {
// ok, you successfully updated the name so why would you go back to the server and get it again? You know the value based on your update.
console.log('Name is updated for ID:' + obj.userId.toString());
// for your example though here is how you could handle it
obj.getUserName(obj, obj.userId, function(user){ // i assumed the name is stored in variable userName
console.log('Name from server = ' + user.userName); // no idea what you are returning but you can figure it out from here
// maybe you also want to capture it again??
obj.name = user.userName;
});
} else {
console.log('Something Wrong');
}
});
}
// pass in captured this as obj, the user id, and a callback
getUserName(obj, userId, callback){
obj.UtilityService.getUserName(userId)
.then(function (result) {
callback(result); // call the callback with the result. The caller can then do something with it
}
}

How to use angularFireCollection initial load callback?

I've been trying to handle properly in my angular service an explicit synchronization with Firebase. I use angularFireCollection with initial load callback function (I need to preload additional data basing on the data returned by the first query). However, I don't know how should I access the fetched data in the callback function:
getGroupIds: function() {
var deferred = $q.defer();
var ref = new Firebase('https://<XXX>.firebaseio.com/groups');
angularFireCollection(ref, function(groups) {
console.log(groups);
deferred.resolve(groups);
});
return deferred.promise;
}
In the above example, how can I access actual data from groups object?d
Thanks in advance for any tips.
A Firebase snapshot is provided as an argument to the callback function, and you can extract the value from it as follows:
angularFireCollection(ref, function(snapshot) {
console.log(snapshot.name() + " has value " + snapshot.val());
});
usually, I'm setting the collection to scope, but either of these seem to work.
var groups = angularFireCollection(ref, function() {
console.log(groups);
});
or in a controller
$scope.groups = angularFireCollection(ref, function() {
console.log($scope.groups );
});

Categories

Resources