I need these values in a row, but I don't want them reply if its already in div.
My try is here. I try to build an array and put these values from firebase to here, but still I can't see in html.
var fruits = [];
var fetchPostsleft = function(postsRef, sectionElement,fruits) {
postsRef .orderByChild('timeStamp').on('child_added', function(data) {
console.log(data.val());
var author = data.val().senderName;
var containerElement2 = sectionElement.getElementsByClassName('posts-containerleft')[0];
fruits.push(data.val().senderName);
console.log(fruits.length);
});
};
fetchPostsleft(topUserPostsRef, topUserPostsSectionleft,fruits);
var fLen = fruits.length;
console.log(fruits.length);
for (var i = 0; i < fLen; i++) {
// text += "<li>" + fruits[i] + "</li>";
topUserPostsSectionleft.getElementsByClassName('posts-containerleft')[0].insertBefore( createheaders(fruits[i], ""),
topUserPostsSectionleft.getElementsByClassName('posts-containerleft')[0].firstChild);
}
The data is loaded from Firebase asynchronously. This means that by the time your looping over the array, it hasn't been populated yet. It's easiest to see this by replacing most of the code with a few log statements:
console.log("Before query");
postsRef.orderByChild('timeStamp').on('child_added', function(data) {
console.log("In child_added");
});
console.log("After query");
If you run this snippet, the logging will be:
Before query
After query
In child_added
This is probably not the order you expected, since it's different from the order the log statements are in your code. This is because the data is loaded from Firebase asynchronously and the rest of your code continues while it's loading.
It's slightly easier to see if you turn the callback into a separate function:
function onChildAdded(data) {
console.log("In child_added");
});
console.log("Before query");
postsRef.orderByChild('timeStamp').on('child_added', onChildAdded);
console.log("After query");
Now you can more easily see that the first few lines just declare the onChildAdded function. They don't run it yet. And we're just passing that function in to the query, so that the query can call onChildAdded whenever it gets the data.
This is the most common pitfall of web programming with remote servers. But since most of the modern web is based on such asynchronous APIs, you will have to learn it.
One way I've found that works is to reframe your problem. Your current code is based on "first fetch the posts, then add them to the HTML". But in asynchronous programming it's better to think "start fetching the posts. When we get them, add them to the HTML". This translates into the following code:
function fetchPostsleft(postsRef, sectionElement) {
postsRef.orderByChild('timeStamp').on('child_added', function(data) {
var author = data.val().senderName;
topUserPostsSectionleft.getElementsByClassName('posts-containerleft')[0].insertBefore(createheaders(author, ""),
});
};
fetchPostsleft(topUserPostsRef, topUserPostsSectionleft);
Now all the code that needs the new data is inside the callback, so it's only run when the snapshot is actually available.
Related
I'm trying to build a translation engine using node.js. I have a Python/R background so I'm not getting the hang of these so called callbacks . . .
The input is a sentence :
var sentence = "I want to translate this"
When I hit the "Translate" button, it should trigger the translation.
Now this is the database query, I call a node.js backend at /translate
function query(string){
var query =
db.call.get(query, function(result){
if(result.length>0){
console.log(result[0].translation);
} else {
// not found in database
console.log(string);
}
});
}
So then it goes like this:
var wordList = sentence.split(" ");
for(i=0; i<wordList.length; i++){
// i call the database to return me the translation for each word
query(wordList[i]);
}
But then, the output in the console log comes like this:
output[0]: "translate", "want", "to", "I"
How can I make it come back in order? I understand there is some async and callback stuff going on, but I do believe that the guys who coded node are smart and that there is surely a way to solve this pretty easily.
Thanks
You need to take into account the fact that your query function might take a different time to return depending on the string argument you pass it. Right now, in your example code, if the query for "want" returns before the query for "I", then your translations will be outputted in the same order that the queries return (i.e. translation of "want", translation of "I").
Callbacks can help you get around this problem, because they are a function that will only get called when your query function returns. Then it's up to you to define what your program should do with the result. For instance, your program can output the results in the same order that the original array was in. This could be achieved using an index as suggested by Guffa, or using the async library that we will discuss later on.
So, one thing you could try is to pass query a callback like this:
function query(string, callback){
db.call.get(query, function(result){
if(result.length>0){
callback(null, result[0].translation);
} else {
// not found in database
callback(null, string);
}
});
}
The canonical way to use callbacks in Node.js, is to give it two parameters, so it looks like: callback(err, result). If there is no error, you can call callback(null, result), while when there is an error, you can call callback(err) or callback (err, result), depending on what you want to report.
Once your query function takes a callback, you are able to know when it did return a value, and you can use the async.map function (from the async library), like so:
var async = require('async');
var sentence = "I want to translate this";
var words = sentence.split(' ');
async.map(words, query, function (err, translations) {
console.log(translations);
}
What this function does, is:
run in parallel query on all the items in the words array
wait for all the callbacks from all the query functions to return
then call async.map's own callback (here the third argument:
function (err, translations) {console.log(translations);})
The only way to make them come back in order would be to chain the calls, but then you take away the point of having asynchronous calls in the first place.
Send along the index for the word also, that way you can put the results together in the right order:
var wordList = sentence.split(" ");
var resultList = [];
var resultCount = 0;
for (i = 0; i < wordList.length; i++){
// i call the database to return me the translation for each word
query(wordList[i], i);
}
function query(string, index){
var query = db.call.get(query, function(result){
if (result.length > 0){
resultList[index] = result[0].translation;
} else {
// not found in database
resultList[index] = string;
}
resultCount++;
if (resultCount == wordList.length) {
console.log(resultList);
}
});
}
I have simple table scripts in Azure written in javascript and node.js.
Now, if I call any of these from Windows Phone, the object parameter gets updated automatically from the return value. And thus code like this works
await table1.InsertAsync(val1);
MyObj val2 = new MyObj();
val2.data = val1.Id;
await table2.InsertAsync(val2);
However now I try to utilize this same from scheduled job in Azure: essentially chaining 2 insert calls so that latter depends on the id of the former. The id column is identity and gets created automatically.
How can I achieve this? Scheduled job is written in javascript/node.js. I have tried
var res = table1.insert(val1);
And using val1.id after the first insert, but neither works.
And of course just moments after I post the question I come up with an answer.
table1.insert(val1, {
success: function(res) {
var val2 = {
data: res.id
}
table2.insert(val2);
}
});
In the Parse JavaScript guide, on the subject of Relational Data it is stated that
By default, when fetching an object, related Parse.Objects are not
fetched. These objects' values cannot be retrieved until they have
been fetched.
They also go on to state that when a relation field exists on a Parse.Object, one must use the relation's query().find() method. The example provided in the docs:
var user = Parse.User.current();
var relation = user.relation("likes");
relation.query().find({
success: function(list) {
// list contains the posts that the current user likes.
}
});
I understand how this is a good thing, in terms of SDK design, because it prevents one from potentially grabbing hundreds of related records unnecessarily. Only get the data you need at the moment.
But, in my case, I know that there will never be a time when I'll have more than say ten related records that would be fetched. And I want those records to be fetched every time, because they will be rendered in a view.
Is there a cleaner way to encapsulate this functionality by extending Parse.Object?
Have you tried using include("likes")?
I'm not as familiar with he JavaScript API as the ObjC API.. so in the example below I'm not sure if "objectId" is the actual key name you need to use...
var user = Parse.User.current();
var query = new Parse.Query(Parse.User);
query.equalTo(objectId, user.objectId);
query.include("likes")
query.find({
success: function(user) {
// Do stuff
}
});
In general, you want to think about reverse your relationship. I'm not sure it is a good idea be adding custom value to the User object. Think about creating a Like type and have it point to the user instead.
Example from Parse docs:
https://parse.com/docs/js_guide#queries-relational
var query = new Parse.Query(Comment);
// Retrieve the most recent ones
query.descending("createdAt");
// Only retrieve the last ten
query.limit(10);
// Include the post data with each comment
query.include("post");
query.find({
success: function(comments) {
// Comments now contains the last ten comments, and the "post" field
// has been populated. For example:
for (var i = 0; i < comments.length; i++) {
// This does not require a network access.
var post = comments[i].get("post");
}
}
});
Parse.Object's {Parse.Promise} fetch(options) when combined with Parse.Promise's always(callback) are the key.
We may override fetch method when extending Parse.Object to always retrieve the relation's objects.
For example, let's consider the following example, where we want to retrieve a post and its comments (let's assume this is happening inside a view that wants to render the post and its comments):
var Post = Parse.Object.extend("Post"),
postsQuery = new Parse.Query(Post),
myPost;
postsQuery.get("xWMyZ4YEGZ", {
success: function(post) {
myPost = post;
}
).then(function(post) {
post.relation("comments").query().find({
success: function(comments) {
myPost.comments = comments;
}
});
});
If we had to do this every time we wanted to get a post and its comments, it would get very repetitive and very tiresome. And, we wouldn't be DRY, copying and pasting like 15 lines of code every time.
So, instead, let's encapsulate that by extending Parse.Object and overriding its fetch function, like so:
/*
models/post.js
*/
window.myApp = window.myApp || {};
window.myApp.Post = Parse.Object.extend("Post", {
fetch: function(options) {
var _arguments = arguments;
this.commentsQuery = this.relation("comments").query();
return this.commentsQuery.find({
success: (function(_this) {
return function(comments) {
return _this.comments = comments;
};
})(this)
}).always((function(_this) {
return function() {
return _this.constructor.__super__.fetch.apply(_this, _arguments);
};
})(this));
}
});
Disclaimer: you have to really understand how closures and IIFEs work, in order to fully grok how the above works, but here's what will happen when fetch is called on an existing Post, at a descriptive level:
Attempt to retrieve the post's comments and set it to the post's comments attribute
Regardless of the outcome of the above (whether it fails or not) operation, always perform the post's default fetch operation, and invoke all of that operation's callbacks
I have Cloud Code I am having trouble linking together... I have been able to utilize Parse effectively for the iOS side on a separate app. I have basic JS skills, throwing the custom Cloud Code in the mix is mucking things up for me. I have read their documentation, and searched for other examples, it is not clicking.
The first function finds objects that meets certain parameters... Works fine.
Parse.Cloud.job("callClient", function(request, response){
var now = new Date();
var message = Parse.Object.extend("Message");
var query = new Parse.Query(message);
query.greaterThan("alertDate", now);
query.find({
success: function(results){
for (var i = 0; i < results.length; i++){
var alertDate = results[i].get("alertDate"); // <- for testing, working
var toPhone = results[i].get("toPhone"); // <- for testing, working
console.log("This is inline- Item number: "+ (i+1) + " Alert date is: " + alertDate + "To Phone: "+ toPhone);
var caller = results[i];
Parse.Cloud.run("testCall",function(request, response) {
}); // <- will fire with with default info
}
response.success("function complete");
}
})
});
The second function is a Twilio feature inside Parse, which also works fine...
Parse.Cloud.define("testCall", function(request, response) {
var client = require('twilio')('Acct SID','Auth Code');
Parse.Cloud.useMasterKey();
client.makeCall({
to:'+17205551212',
from:'+17205551213',
url:'http://TWIml-File.xml',
method:'GET'
}, function(err, responseData){
if (err){
response.error("fail");
}else{
response.success("success");
}
});
});
I realize I cannot pass an object to a cloud code function, nor does a global variable persist. So how do I get the results of "callClient" visible to "testCall"?
According to the Parse staff, it is not possible to maintain a global state across different cloud module sessions.
I run into a similar issue, and I have decided to follow their tip.
Parse cloud javascript global variable
Problem solved...
I was depending too much on the cloud code itself. I thought I needed it for all the functions I was trying to do, based on what I interpreted in the documentation.
I discarded "testCall" as a cloud code function, and made it a regular JS function, and embedded the other stuff inside it and works great.
I have a Firebase with a users reference, which has a field user_id.
([Firebase]/[app]/users/user_id)
I insert a user & assign a user_id. However, when I read the value of user_id from userRef (a reference to users), it does not read it the first time. Subsequent reads work perfectly fine.
Using the debugger, I can see that when the user is created, a user_id is assigned. It seems like my first call refreshes the Firebase reference, so that subsequent calls are now seeing the udpated Firebase (I don't think that is what it is - that is just how it appears).
This is my code to read the value of user_id:
var userID = 0;
userRef.on('value', function(snapshot) {
userID = snapshot.val().user_id;
});
alert(userID);
The first time, the alert shows 0. Every time after that, it shows the correct value.
To make the problem even stranger, I also have a games reference with a game_id, created in the same way as user. But my read on game_id (in the exact same way & at the same time) works every time.
Any ideas?
The issue here is that .on() doesn't (in general) trigger your callback function immediately. In particular, the first time you do a .on() at a location, Firebase has to go ask the server for the current value, wait for the response, and THEN call your callback. And it does this all asynchronously.
The way your code is currently written, "alert(userID);" is being run before your callback code ("userID = snapshot.val().user_id;") so it always reports 0 the first time. The simple fix is to move the alert() inside your callback:
var userID = 0;
userRef.on('value', function(snapshot) {
userID = snapshot.val().user_id;
alert(userID);
});
Here's a common methodology to wait on two callbacks, using using jQuery's Promise model and once:
var userID = 0;
// sends a callback to fx where the results can be stored
function defer( fx ) {
return function() {
var deferred = $.Deferred();
fx( function(snapshot) { deferred.resolve(snapshot.val(); } );
return deferred.promise();
}
}
$.when( // wait for both actions to complete
defer( function(callback) { userRef.once('value', callback) }),
defer( function(callback) { widgetRef.once('value', callback) })
).then( function(values) {
// both deferreds are done now
// and values contains an array with the snapshot.val() from each call
console.log(values);
});