Windows 8 Javascript Promises - html

I have the following function:
function pickContacts() {
var output = "";
// Create the picker
var picker = new Windows.ApplicationModel.Contacts.ContactPicker();
picker.commitButtonText = "Select";
var emailsPromise = new WinJS.Promise(function () {
// Open the picker for the user to select contacts
picker.pickMultipleContactsAsync().then(function (contacts) {
if (contacts.length > 0) {
// Get selected e-mails
contacts.forEach(function (contact) {
contact.emails.every(function (email) { output += email.value + ";"; });
});
return output;
} else {
return "";
}
});
});
return emailsPromise;
};
This gets me a list of e-mail addresses from selected contacts. So next I want to use that; here's my code:
document.getElementById("findEmail").addEventListener("click", function () {
var emailAdd = document.getElementById("email");
pickContacts().done(function (emails) {
emailDets.value = emails;
});
});
But I'm not getting the return value from pickContacts (which I've determined is actually returned). I'm guessing that there's something wrong with the way I'm handling the returned promise, but I can't seem to debug it (trying to step into it just exits the function).
What am I doing wrong?

You don't need to create a new promise--just return the promise from pickMultipleContactsAsync.then. The promises spec says that the return value from .then is another promise that's fulfilled when your completed handler finishes, and the fulfillment value is the return value of the completed handler. So you can just do this:
return picker.pickMultipleContactsAsync().then(function (contacts) {
if (contacts.length > 0) {
// Get selected e-mails
contacts.forEach(function (contact) {
contact.emails.every(function (email) { output += email.value + ";"; });
});
return output;
} else {
return "";
}
});
The other way works, but just creates yet another promise that isn't needed, and makes your code a little more complex.

You'll have to pass the completed callback as a parameter of your promise function and then call it with the results you want to pass. Esseintially:
var emailsPromise = new WinJS.Promise(function () {
// Open the picker for the user to select contacts
picker.pickMultipleContactsAsync().then(function (contacts) {
if (contacts.length > 0) {
// Get selected e-mails
contacts.forEach(function (contact) {
contact.emails.every(function (email) { output += email.value + ";"; });
});
return output;
} else {
return "";
}
});
});
becomes:
var emailsPromise = new WinJS.Promise(function (complete, error, progress) {
// Open the picker for the user to select contacts
picker.pickMultipleContactsAsync().then(function (contacts) {
if (contacts.length > 0) {
// Get selected e-mails
contacts.forEach(function (contact) {
contact.emails.every(function (email) { output += email.value + ";"; });
});
complete(output);
} else {
complete("");
}
});
});

Related

variable value becomes undefined in NodeJS ExpressJS

I am working with NodeJS using ExpressJS framework in a mysql backend. I am running a query inside a for loop and my loop and work afterwards depends on the return value of the query. I am not very good with mysql query so I am running it through a for loop.
The problem is, due asynchronous [I guess!], the for loop ends long before the query result comes out.
Here is my code:
function search_people_step2(user_id, search_criteria, user_friend)
{
var first_name_friends = [];
var last_name_friends = [];
for(var i = 0; i < user_friend.length; i++)
{
con.query("SELECT first_name, second_name FROM user WHERE userid = ?", user_friend[i],function(err, rows)
{
if(err)
{
//error;
}
else
{
if(rows.length == 0)
{
//nothing gets returned
}
else {
console.log(rows);
first_name_friends[i] = rows[0].first_name;
last_name_friends[i] = rows[0].second_name;
}
}
});
}
Now,I can get the value (using console.log) inside the query statement, however, on the outside, the value becomes empty (undefined) since the rest of the code has already been computed.
How can I solve this?
Thanks in advance.
The first thing that I find weird in your code is that you are not using an IN statement in your SQL query (not directly related to your problem though) which means you are making as many requests as there are entries in user_friend. The problem is that the SQL library is implemented asynchronously and you cannot avoid it. BUT you can handle it elegantly with Promises which are ES6 features:
(I didn't test the code but I think it should work)
function search_people_step2(user_id, search_criteria, user_friend)
{
return new Promise((resolve,reject)=>{
var first_name_friends = [];
var last_name_friends = [];
var placeHolders=user_friend.map(()=>"?").join(",");
con.query("SELECT first_name, second_name FROM user WHERE userid IN ("+placeHolders+")",user_friend,(err,rows)=>{
if(err)
reject(err);
else{
rows.forEach(row=>{
first_name_friends.push(row.first_name);
last_name_friends.push(row.second_name);
});
resolve({first_name_friends,last_name_friends});
}
});
});
}
And call your function like this :
search_people_step2(id,crit,friends).then(result=>{
//handle result asynchronously as there is no choice
console.log(result.first_name_friends);
console.log(result.last_name_friends);
}).catch(err=>{
//handle error
});
You are right, your problem is the asynchronous nature of the mysql call. You have to provide a callback to your search_people_step2 function.
You may change it like this:
search_people_step2(user_id, search_criteria, user_friend, callback)
In your function body you may use a library called async to handle all the callbacks properly. Here is an example for the usage:
async.eachSeries(user_friend, function(item, eachCb){
con.query("SELECT first_name, second_name FROM user WHERE userid = ?",
user_friend[i],function(err, rows) {
if(err) {
eachCb('error');
}
else {
if(rows.length == 0){
//nothing gets returned
eachCb(null);
}
else {
console.log(rows);
first_name_friends.push(rows[0].first_name);
last_name_friends.push(rows[0].second_name);
eachCb(null);
}
}
}, callback);
});
This calls each query in order on every item of the array and calls the inner callback if finished. When all items are processed or an error occured the outer callback is called. See the async library for further documentation.
simplest solution is
function search_people_step2(user_id, search_criteria, user_friend)
{
var first_name_friends = [];
var last_name_friends = [];
for(var i = 0; i < user_friend.length; i++)
{
con.query("SELECT first_name, second_name FROM user WHERE userid = ?", user_friend[i],function(err, rows)
{
if(err)
{
//error;
}
else
{
if(rows.length == 0)
{
//nothing gets returned
}
else {
console.log(rows);
first_name_friends[i] = rows[0].first_name;
last_name_friends[i] = rows[0].second_name;
}
if(i==user_friend.length-1){
//do your work here which you want to perform in end
}
}
});
}
or use async library
var async = require('async');
var first_name_friends = [];
var last_name_friends = [];
async.series([function(cb){
function search_people_step2(user_id, search_criteria, user_friend)
{
for(var i = 0; i < user_friend.length; i++)
{
con.query("SELECT first_name, second_name FROM user WHERE userid = ?", user_friend[i],function(err, rows)
{
if(err)
{
//error;
}
else
{
if(rows.length == 0)
{
//nothing gets returned
}
else {
console.log(rows);
first_name_friends[i] = rows[0].first_name;
last_name_friends[i] = rows[0].second_name;
}
if(i==user_friend.length-1){
cb()
}
}
});
}
},function(cb){
//do your work here
}],function(err){})

AngularJS - Weird error with sending and deleting JSON data from cache in while loop?

The loop is behaving strange where the alerts are firing out of order and the JSON data is being sent all at the same exact time. I do not understand why this is happening at all. I have been struggling with this for too long now and any help would be insanely appreciated!
Submitting with 3 cached JSON objects, the sequence goes:
Alert "should be second"
Alert "should be second"
Alert "should be second"
Alert "{#xmlns:ns3":"url}"
Alert "should be first"
Alert "0posted"
Then successfully sends all three JSON objects to the database at the same time.
The cachePostCount is now set to zero
app.controller('FormCtrl', function($scope, $filter, $window, getData, Post, randomString) {
// Get all posts
$scope.posts = Post.query();
// Our form data for creating a new post with ng-model
$scope.postData = {};
$scope.$on('updateImage', function () {
$scope.postData.attachment = getData.image;
});
$scope.postData.userid = "Mango Farmer";
$scope.postData.uuid = randomString(32); //$scope.genUUID();
$scope.$on('updateGPS', function () {
$scope.postData.gps = getData.gps;
});
$scope.postData.devicedate = $filter('date')(new Date(),'yyyy-MM-dd HH:mm:ss');
$scope.newPost = function() {
var post = new Post($scope.postData);
var postCount = window.localStorage.getItem("cachedPostCount");
if(typeof postCount == 'undefined' || postCount == null){
postCount = 1;
window.localStorage.setItem("cachedPostCount", postCount);
}
else {
postCount ++;
window.localStorage.setItem("cachedPostCount", postCount);
}
window.localStorage.setItem("post" + postCount, JSON.stringify(post));
while (postCount > 0) {
var curCacheObj = new Post(JSON.parse(window.localStorage.getItem("post" + postCount) || '{}'));
curCacheObj.$save().then(function(response) {
var servResponse = JSON.stringify(response);
alert(servResponse);
if (servResponse.indexOf("#xmlns:ns3") > -1) {
alert("should be first");
window.localStorage.removeItem("post" + postCount);
alert(window.localStorage.getItem("cachedPostCount") + "posted");
$window.location.href = 'success.html';
}
else {
alert("Unable to post at this time!");
}
});
alert("should be second");
postCount --;
window.localStorage.setItem("cachedPostCount", postCount);
}
};
$save() is an asynchronous operation and is guaranteed to not happen until after the next tick in the event loop, which will occur after alert("should be second"); occurs. You should place this alert (and any other logic) that relies on that ordering inside the then() function or chain on another then() function and put it in there instead, like so:
curCacheObj.$save().then(function(response) {
var servResponse = JSON.stringify(response);
alert(servResponse);
if (servResponse.indexOf("#xmlns:ns3") > -1) {
alert("should be first");
window.localStorage.removeItem("post" + postCount);
alert(window.localStorage.getItem("cachedPostCount") + "posted");
$window.location.href = 'success.html';
}
else {
alert("Unable to post at this time!");
}
}).then(function() {
alert("should be second");
postCount --;
window.localStorage.setItem("cachedPostCount", postCount);
});
The problem was that .$save() does not like while loops (maybe because it is an asynchronous function as mentioned previously). I recreated the effect of a while loop with a function using an if statement that will fire the function again if the cached postCount still has data as follows:
$scope.submitAndClearCache = function() {
var postCount = window.localStorage.getItem("localPostCount");
var curCacheObj = new Post(JSON.parse(window.localStorage.getItem("post" + postCount) || '{}'));
if (postCount != 0) {
curCacheObj.$save().then(function(response) {
alert(response);
alert("Post " + postCount + " sent!");
}).then(function() {
postCount --;
window.localStorage.setItem("localPostCount", postCount);
postCount = window.localStorage.getItem("localPostCount");
$scope.submitAndClearCache();
});
}
};
$scope.addCachePost = function() {
var frmData = new Post($scope.postData);
var postCount = window.localStorage.getItem("localPostCount");
postCount ++;
window.localStorage.setItem("localPostCount", postCount);
window.localStorage.setItem("post" + postCount, JSON.stringify(frmData));
};
This technique works, it just seems weird.

chrome cast on chrome not sending message

if (!chrome.cast || !chrome.cast.isAvailable) {
setTimeout(initializeCastApi, 1000);
}
function initializeCastApi() {
var sessionRequest = new chrome.cast.SessionRequest(applicationID);
var apiConfig = new chrome.cast.ApiConfig(sessionRequest,
sessionListener,
receiverListener);
chrome.cast.initialize(apiConfig, onInitSuccess, onError);
};
function sessionListener(e) {
//this function doenot runs firsttime
appendMessage('New session ID:' + e.sessionId);
session = e;
session.addUpdateListener(sessionUpdateListener);
session.addMessageListener(namespace, receiverMessage);
console.log(receiverMessage);
}
sessionListener() function doenot gets called first time.when session get updaed it gets called.Why is it so?

Why can't my html5 class find my observable array contents?

Thanks in advance for any help. I have spent a couple of weeks scouring the web for some insight. I have been developing code for over 50 years but I am a fairly new JavaScript, HTML, knockout. From what I see, this will be great if I can figure out how to make it work. The example given below is only one of the many things I have tried. Please advise.
I have defined two variables as observables, one computed observable, and one observableArray in my view model. Within the document.ready function, I make an Ajax call which returns a json in object notation. ( I checked it in the debugger). When my HTML page displays the observables and computed observables show up properly. The
observable array generates an error (see below) and then displays data obtained from only the first row returned from Ajax. (two were returned).
How do I adjust my code so that all the rows in the Ajax data are shown in the displayed HTML?
Here is the error message that I get:
Uncaught ReferenceError: Unable to process binding "foreach: function (){return cartItems }"
Message: Unable to process binding "text: function (){return itemSubTotal() }"
Message: itemSubTotal is not defined (19:41:39:172 | error, javascript)
Here is my global data for the view model:
var cartDB = "";
var totalPrice = "100";
var cartItems = new Array;
Here is the view model:
var ViewModel =function (){
// direct items ==
this.cartDB = ko.observable(cartDB);
// array itesm
// this.cartItems = ko.observableArray(cartItems);
this.cartItems = ko.mapping.fromJS(cartItems);
//for (var n = 1;n++;n<cartItems.length){ this.cartItems.push(cartItem[n]);}
// computed items
this.totalPriceSv = ko.computed(function(){
return "Total Price*=" + centsToDollars(totalPrice);// todo fix
} ,this);//end totalSvPrice
};// end ViewModel
The data is obtained from the following routine which calls on Ajax.This routine is called once from within document.ready and obtains the expected data on the success callback.
function databaseCart(commandInput, cartDBInput, cartPidInput,logPhpInput) {
var postData = new Array();
postData[0] = commandInput;
postData[1] = cartDBInput;
postData[2] = cartPidInput;
postData[3] = logPhpInput; //debug log on php side
data = null; //for return values
$.ajax({
type: "GET",
url: 'ww_CartProcess.php', //the script to call to get data
data: {data: postData},
dataType: 'json',
success: function(data) {
cartItems = data;
debug = 0;
},
complete: function(data) {
ko.applyBindings(new ViewModel);
return TRUE;
},
error: function(x, e) {//this is the error function for ajax
var xErr = x;
var eErr = e;
ajaxErrorProcessing(xErr, eErr, "addToCart", postData);
}
});
}// end databasecart
Here is the HTML 5 snippet.
<div>
<h1 id="cartTitle">Cart Number: <span data-bind="text: cartDB"> </h1>
<div class ="boxCartItem" data-bind="foreach:cartItems" >
<div class ="boxItemTitle">
<h2 data-bind="text: title()"></h2>
</div><!--boxItemTitle-->
<div class ="cartItemBottom"></div>
</div ><!--class ="boxCartItem"-->
My thanks to the commenters. I still do not know how to add an element to all item rows in an observable array, but this problem was caused by not having the item listed defined. Clue> When multiple errors are presented it is sometimes ( and maybe always) good to work from the bottom up.
The problem can be better stated as : Given a 2 x 17 array (2 rows and 17 columns of independent vars)create an observableArray that contains 2 rows and 17 plus columns consisting of the 17 independent variables (can only be changed in the database or by limited user input) augmented with a large number of computed functions .
1.0 I created an orderModel which contained the ko.computed(functions() for each dependent variable.
function rowOrder(data) {
var self = this;
var model = ko.mapping.fromJS(data, {}, self);
model.imagePathSv = ko.computed(function() {
var category = self.category();
var imageName = self.imageName();
var sv ="";
var sv = "products\\" +category+"\\" + imageName;
return sv;
});//end model.imagePathSv
//original offer
model.origNumValidate = ko.computed(function() {
ans = self.origNum();
if (ans < 0 | ans > 1) {
alert("\
Only onw Original can be pruchased");
ans = 1;
}
self.origNum(ans);
return ans;
});//originalNumValidate
model.origNumLabel = ko.computed(function() {
var sv = "Original..." + centsToDollars(self.origCost());
return sv;
});//end model.origNumLabel
model.origMattedShow = ko.computed(function() {
if (self.origMattedCost() > 0) {
return true;
}
else {
return false;
}
});
model.origMattedLabel = ko.computed(function() {
var sv = "Matting...." + centsToDollars(self.origMattedCost());
return sv;
});
model.origFramedShow = ko.computed(function() {
if (self.origFramedCost() > 0) {
return true;
}
else {
return false;
}
});
model.origFramedLabel = ko.computed(function() {
var sv = "Framing...." + centsToDollars(self.origFramedCost());
return sv;
});
//reproductons offer
model.reproNumValidate = ko.computed(function() {
ans = self.reproNum();
self.reproNum(ans);
return ans;
});
model.reproNumLabel = ko.computed(function() {
var sv = "Reproductions." + centsToDollars(self.reproCost()) + " each";
return sv;
});//end model.reproNumLabel
model.reproMattedShow = ko.computed(function() {
if (self.reproMattedCost() > 0) {
return true;
}
else {
return false;
}
});
model.reproMatted = ko.observable(true);
model.reproMattedLabel = ko.computed(function() {
var sv ="Matting...." +centsToDollars(self.reproMattedCost());
return sv;
});
model.reproFramedShow = ko.computed(function(){
if(self.reproFramedCost()>0){return true;}
else {return false;}
});
model.reproFramed = ko.observable(true);
model.reproFramedLabel = ko.computed(function() {
var sv ="Framing...." +centsToDollars(self.reproFramedCost());
return sv;
});
//pricing subTotals
model.productsSubTotal = ko.computed(function() {
var ans =self.origNum() * self.origCost() + self.reproNum() * self.reproCost();
return ans;
});//end model.productsSubTotal
model.productsSubTotalSv = ko.computed(function() {
return "Products.." +centsToDollars(model.productsSubTotal());
return ans;
});//end model.productsSubTotal
model.mattingSubTotal = ko.computed(function() {
return self.origNum() * self.origMattedCost() + self.reproNum() * self.reproMattedCost();
});//end model.mattingSubTotal
model.mattingSubTotalSv = ko.computed(function() {
return "Matting...." +centsToDollars(model.mattingSubTotal());
});//end model.mattingSubTotal
model.framingSubTotal = ko.computed(function() {
return self.origNum() * self.origFramedCost() + self.reproNum() * self.reproFramedCost();
});//end model.framingSubTotal
model.framingSubTotalSv = ko.computed(function() {
return "Framing...." +centsToDollars(model.framingSubTotal());
});//end model.productsSubTotal
model.rowSubTotal = ko.computed(function() {
return model.productsSubTotal() +model.mattingSubTotal() + model.framingSubTotal();
});//end model.rowsSubTotal
model.rowSubTotalSv = ko.computed(function() {
return "Item Subtotal.." +centsToDollars(model.rowSubTotal());
});//end model.productsSubTotal
};// END rowOrder
2.0 I created a mapping variable as follows:
var mapping = {
create: function(options) {
return new rowOrder(options.data);
}
};
3,0 I created a View Model as follows:
function ViewModel() {
// direct items for whole page
var self = this;
this.cartId = ko.observable(cartDB);
this.cartIdSv = ko.computed(function() {
var sv = "Cart Number: "+ self.cartId();
return sv;
},this);//
this.totalPrice = ko.computed(function() {//to DO COMPUTE
var int = 10000;
return int;
},this);
this.totalPriceSv = ko.computed(function(){
return "Total Price*: " + centsToDollars(this.totalPrice());
},this);
// by row items
this.cartItems = ko.mapping.fromJS(cartItems, mapping);
}// end ViewModel
4.0 In the success call back from ajax :
success: function(data) {
cartItems = data;
ViewModel();
5.0 I put the ko.apply.bindings(new ViewModel) in the ajax complete callback.
The result was that my rather involved page came up as expected with the computed values initially set.
I am still working on how to update this page. I have not been able to get my code to recompute the computed variables when the user clicks or unclicks a checkbox.
I could not have done this without suggestions from the stackOverflow group. Thanks to you all for the posts that I found all over the google.

How do I return the number of rows of a table according to a search criteria in Web SQL?

I have created a database called todo with table name todo having fields like title,date etc. The table is empty right now.
I have defined a function which takes title as parameter and check whether table contains that title or not.
It should return the number of rows.
GetTitle function:
todo.webdb.GetTitle = function(title) {
var db = todo.webdb.db;
db.transaction(function(tx){
tx.executeSql("SELECT title FROM todo WHERE title=?", [title],
loadTitle,
todo.webdb.onError);
});
}
loadTitle Function:
function loadTitle(tx, rs) {
return rs.rows.length;
}
GetTitle Function is called:
row=todo.webdb.GetTitle("Hello");
alert(row);
I get alert 'undefined' it should return 0
I am confused right now how to resolve this issue.
I think the transactions are asynchronous, so you need to callback function to receive the value.
todo.webdb.GetTitle = function(title, callback) {
var db = todo.webdb.db;
db.transaction(function(tx){
tx.executeSql("SELECT title FROM todo WHERE title=?", [title],
(function loadTitle(tx, rs){
callback( rs.rows.length );
}),
todo.webdb.onError);
});
}
todo.webdb.GetTitle( "Hello", function(count){
alert( "count = " + count );
});
Here's an excellent tutorial for more information.
http://blog.darkcrimson.com/2010/05/local-databases/
Update
Don't make functions inside a loop for this main reason.
Both i and title are referencing a value created out of local scope.
So the last value of assigned to both i and title will be displayed.
for (i = 0; i < itemcount; i++) {
alert('i outside if:' + i);
var title = x[i].getElementsByTagName("title")[0].childNodes[0].nodeValue;
todo.webdb.GetTitle( title, function (count) {
if (!count) {
alert('i inside if:' + i);
alert( title );
}
});
}
Fix:
Generate a function that has the values locked in a closure.
var createTitleCallBack = function( i, title ){
return function (count) {
if (!count) {
alert('i inside if:' + i);
alert( title );
}
};
};
for (i = 0; i < itemcount; i++) {
alert('i outside if:' + i);
var title = x[i].getElementsByTagName("title")[0].childNodes[0].nodeValue;
todo.webdb.GetTitle( title, createTitleCallBack( i, title ) );
}