Understanding Angular 2 observables synchronization - javascript

I have registration form which should be validated before submission. For this purpose I have method, which pushes error to error array. If error length is zero, I send this form to the server, otherwise I show list of errors.
signUpForm() {
this.validateOnSubmit();
console.log(this.TAG + 'submit method fired! ');
console.log('errors array' + JSON.stringify(this.errors));
if (this.errors.length == 0) {
/* Sending process*/
} else {
this.showOnSubmitError(this.errors);
}
}
Address part of form is validated with request to Google api via Observables.
validateOnSubmit() {
let fullAddress = this.regModel.Addresses[0].State + ', ';
fullAddress += this.regModel.Addresses[0].Street + ', ';
fullAddress += this.regModel.Addresses[0].City + ', ';
fullAddress += this.regModel.Addresses[0].Zip + ' ';
this.signUpHelperProvider.resolveAddr(fullAddress)
.subscribe(response => {
this.geoCodeResp = response;
console.log(this.TAG + 'Before submission: check received geocode response stringify: ' + JSON.stringify(this.geoCodeResp));
if (this.geoCodeResp.status != 'OK' || this.geoCodeResp.results[0].address_components.length == 1) {
console.log('WE HAVE A PROBLEM');
this.errors.push('Please, check if your address correct');
console.log('WE HAVE A PROBLEM error:' + this.errors.toString());
} else {
this.regModel.Addresses[0].Longitude = this.geoCodeResp.results[0].geometry.location.lng;
this.regModel.Addresses[0].Latitude = this.geoCodeResp.results[0].geometry.location.lat;
}
});
//
//other checks
//
console.log('total errors in method: ' + this.errors.toString());
}
And here is the problem: actual check of error length happens before validation method completed.
I: [INFO:CONSOLE(9)] "SignUpHelperProvider: resolveAddr: address passed NY, Hfjdir6rhc, Durfjfu, 35682 ", source:
I: [INFO:CONSOLE(14)] "total errors in method: ",
I: [INFO:CONSOLE(13)] "SignUpPage: submit method fired! ",
I: [INFO:CONSOLE(13)] "errors array[]",
I: [INFO:CONSOLE(14)] "SignUpPage: Before submission: check received geocode response stringify: {"results":[{"address_components":[{"long_name":"United States","short_name":"US","types":["country","political"]}],"formatted_address":"United States","geometry":{"bounds":{"northeast":{"lat":71.5388001,"lng":-66.885417},"southwest":{"lat":18.7763,"lng":170.5957}},"location":{"lat":37.09024,"lng":-95.712891},"location_type":"APPROXIMATE","viewport":{"northeast":{"lat":49.38,"lng":-66.94},"southwest":{"lat":25.82,"lng":-124.39}}},"partial_match":true,"place_id":"ChIJCzYy5IS16lQRQrfeQ5K5Oxw","types":["country","political"]}],"status":"OK"}",
I: [INFO:CONSOLE(14)] "WE HAVE A PROBLEM",
I: [INFO:CONSOLE(14)] "WE HAVE A PROBLEM error:Please, check if your address correct",
Is there any way to synchronize this process? I'm new to Angular 2 and Ionic 2 and will appreciate any hint or help.

Your signUpHelperProvider.resolveAddr is asynchronous. All you have to do is to do the sending process on subscription. One way to do it is to use map in place of subscribing within the validateOnSubmit and return the observable.
Like so -
validateOnSubmit() {
let fullAddress = this.regModel.Addresses[0].State + ', ';
fullAddress += this.regModel.Addresses[0].Street + ', ';
fullAddress += this.regModel.Addresses[0].City + ', ';
fullAddress += this.regModel.Addresses[0].Zip + ' ';
//return async op to subscribe
return this.signUpHelperProvider.resolveAddr(fullAddress)
.map(response => {
this.geoCodeResp = response;
console.log(this.TAG + 'Before submission: check received geocode response stringify: ' + JSON.stringify(this.geoCodeResp));
if (this.geoCodeResp.status != 'OK' || this.geoCodeResp.results[0].address_components.length == 1) {
console.log('WE HAVE A PROBLEM');
this.errors.push('Please, check if your address correct');
console.log('WE HAVE A PROBLEM error:' + this.errors.toString());
} else {
this.regModel.Addresses[0].Longitude = this.geoCodeResp.results[0].geometry.location.lng;
this.regModel.Addresses[0].Latitude = this.geoCodeResp.results[0].geometry.location.lat;
}
//
//other checks
//
console.log('total errors in method: ' + this.errors.toString());
return response;//return the response in case required at subscription
});
}
Your signUpForm will be:
signUpForm() {
this.validateOnSubmit().subscribe(response=>{
console.log(this.TAG + 'submit method fired! ');
console.log('errors array' + JSON.stringify(this.errors));
if (this.errors.length == 0) {
/* Sending process*/
} else {
this.showOnSubmitError(this.errors);
}
});
}
IMO you need to perform such async calls in a provider

Related

googlemapsapi returns status:INVALID_REQUEST and an empty JSon object

I am using node.js to develop firebase cloud functions. I need to connect to google maps api to get the distance between two Latlng points. I am making an https get request and receiving a response with a JSon object. The problem is that the received object is empty with a status: INVALID_REQUEST in most cases. However, in some rare cases it returns the desired value. I have tried the path and host of my request on the browser and the json object is retrieved successfully there. I do not know exactly where my problem is. Is it in the callback? the path? something else?
I am giving my code and the output of it.
My code is :
function getStatusCode(options, callback) {
https.get(options, function(http_res) {
var data = "";
console.log('inside the https request');
http_res.on("data", function (chunk) {
data += chunk;
console.log("I am reading the data");
console.log(data);
// callback(http_res.statusCode, data)
});
http_res.on("end", function () {
console.log("I am in the ON_END listener");
console.log('data contains: >> ' + data + ' I am in the ONEND listener')
callback(http_res.statusCode, data)
});
});
}
and I am calling it as follows:
console.log('startingPoints ' + startingPoints);
console.log('lat and lng are: '+lat+" , "+lng);
var options = {
host: 'maps.googleapis.com',
path: '/maps/api/distancematrix/json?units=imperial&origins='+startingPoints+'&destinations='+lat+','+lng+'&key=MY_GOOGLEMAPSAPI_KEY',
method: get
};
getStatusCode(options, function(statusCode, data){
console.log('The status code is : '+statusCode);
console.log('and data is : '+data);
// parsing json object:
jData = JSON.parse(data);
rows = jData.rows;
console.log('the length of the rows array is >> ' + rows.length + ', the length of the techs array is >> ' + techs.length);
min = -1;
for(var i = 0; i < rows.length; i++){
console.log('the array of techs + the equivalent values of the array of row >>' + techs[i] + ' and ' + rows[i].elements[0].distance.value);
if( min < 0 || rows[i].elements[0].distance.value < rows[min].elements[0].distance.value)
min = i;
console.log('minimum distance tech in the loop; the id is >> ' + techs[min] + ", and the distance is >> " + rows[min].elements[0].distance.value);
}
console.log('the min value before return is >> ' + min);
and the retrieved json object is:
{
"destination_addresses" : [],
"origin_addresses" : [],
"rows" : [],
"status" : "INVALID_REQUEST"
}
any idea please,,
I have found a solution. precisely, found my problem. The problem was not within google-map-api. It was with assignment of the starting_points variable.

Unit testing a function and simulating an event emitter response

I am currently writing some tests against the code shown below in node.js using mocha. I want to simulate the response from the event emitter 'check_user'. I have sinon.js but I can't get my mind in the right place to work out the best way to simulate the response.
Anyone able to offer some advice on how to go about this?
TgCustomCommand.contact = new Command("contact", "Will send you a users contact card to add to your contact list. Usage is .contact nick", function (input, callback) {
var payload = input;
//Switch our contact to payload.to as this is what our checkuser function looks at
//Check the command over
if (payload.command_args === false) {
payload.response = 'msg ' + payload.to + ' ' + this.description;
return callback(null, payload);
} else {
payload.return = payload.to;
payload.to = payload.command_args; //Set up ready for nick check
//Check the nick exists
emitter.emit('check_user', payload, function (err, result) {
if (err) return callback(err, null);
payload = input; //Reset our payload so we have correct payload.to
//Check how many users we returned
if (result.length === 0) { //Not in our contact list
payload.response = 'msg ' + payload.return + ' I do not have that person in my contact list!';
return callback(null, payload);
} else if (result.length === 1) {
payload.to = result[0].Nick;
payload.response = "send_contact " + payload.return + ' ' + result[0].Phone + ' ' + result[0].Nick + " _";
return callback(null, payload);
}
else {
//loop through our object and create a list of those returned
payload.response = "msg " + payload.return + " I know multiple people with a similar nick: ";
for (var i = 0; i < result.length; i++) {
log.debug(result[i].Nick);
payload.response = payload.response + result[i].Nick + " ";
}
return callback(null, payload);
}
});
}
;
});

How to return a value from WinJS

I am developing an app which consumes a WebService like this
function callWS(filebytes, fpath, filename) { //consumes the webservice
var response;
var data = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://ws.myCompany.com">\n' +
' <soapenv:Header/>\n' +
' <soapenv:Body>\n' +
' <ws:uploadFileService>\n' +
' <ws:filebytes>' + filebytes + '</ws:filebytes>\n' +
' <ws:fpath>' + fpath + '</ws:fpath>\n' +
' <ws:filename>' + filename + '</ws:filename>\n' +
' </ws:uploadFileService>\n' +
' </soapenv:Body>\n' +
'</soapenv:Envelope>\n';
console.log("XML SOAP: " + data + "\r\n");
var options = {
url: "http://XXX.XXX.XX.XXX:XXXX/FILESERVERWS/services/FILESERVERWS?wsdl",
type: "post",
headers: {
"Content-Type": "text/xml; charset=utf-8",
"SOAPAction": "uploadFileService"
},
data: data
};
WinJS.Promise.timeout(8000, WinJS.xhr(options)).then(
function (request) {
var doc = request.responseXML.documentElement;
var output = doc.getElementsByTagName("uploadFileServiceReturn");
//Windows.UI.Popups.MessageDialog(output[0].textContent, "the XML message").showAsync();
console.log("the XML message: " + output[0].textContent + "\r\n");
result.style.backgroundColor = "#00A000";
response = true;
},
function (error) {
Windows.UI.Popups.MessageDialog(error.status + " : " + error.statusText, "Status").showAsync();
result.style.backgroundColor = "#FF0000";
response = false;
},
function (progress) {
result.innerText = "Ready state is " + progress.readyState;
result.style.backgroundColor = "#0000A0";
}
);
return response;
}
the purpose is to consume the webService and returns a value
on success response = true
on error response = false
because I want to take an action depending if the webService returned a value by doing this
if (callWS(document.getElementById("formfield" + i).value, UID_KEY[7], arrayCaptures[i - 1].name)) {
console.log("take action a");
}
else {
console.log("take action b");
}
but it always take action B even if the webService is consumed and I get answer from the webservice, what am I doing wrong???
You'll need to return a Promise object from your function, allowing the calling script to use a then() or done() call on it to get the result. You can read more about asynchronous programming in WinJS on the msdn site, but generally it looks like this:
function callWS(filebytes, fpath, filename) {
return new WinJS.Promise(function (complete, error) {
// put your functionality here...
WinJS.Promise.timeout(8000, WinJS.xhr(options)).then(
function (request) {
// more processing...
complete(true); // or false or a variable...
},
function (e) {
// error handling unique to this function
complete(false); // OR you could just call error(e);
},
...
);
});
}
And you would use it like this:
callWS( ... ).then(
function(response) {
// handle response...
// will be either true or false
},
function(err) {
// handle errors
}
);

ContextMenuItem context function is not executing

I want my context menu item to be visible only if the clicked node is a link i.e. and href is either a magnet link or a torrent link. But item is visible for all the links because context function is not executing, can anybody help why context function is not executing?
Here is the code:
exports.main = function() {
var cm = require("sdk/context-menu");
var contextCode = ' self.on("context", function (node) { '+
' while(node.nodeName!="A") { node = node.parentNode; } '+
' var pat_magnet = /^magnet:/i; ' +
' var pat_torrent = /.torrent$/i; ' +
' if(pat_torrent.test(node.href) || pat_magnet.test(node.href)) { return true; } '+
' else { return false; } '+
' }); ';
var clickCode = ' self.on("click", function(node,data){ '+
' while(node.nodeName!="A") { node = node.parentNode; } '+
' var pat_hash = /[0-9abcdef]{32,40}/i; ' +
' var result = node.href.match(pat_hash); '+
' var hash = "" '
' if(result != null) { hash=result[0]; } '+
' var xhr = new XMLHttpRequest(); '+
' if(hash != "") { '+
' var apiCall = "https://www.furk.net/api/dl/add?api_key=*************&info_hash="+hash; '+
' } '+
' else{ '+
' var apiCall = "https://www.furk.net/api/dl/add?api_key=*************&url="+encodeURI(node.href); '+
' } '+
' xhr.open("GET",apiCall,true); '+
' xhr.onreadystatechange = function(){ if(xhr.readyState = 4) { if (xhr.response.status = "ok") { alert("Torrent added to Furk."); } else { alert("Torrent could not be added to Furk."); } } } '+
' xhr.send(null); '+
' });';
cm.Item({
label: "Add to Furk",
context: cm.SelectorContext("a[href]"),
contentScript: contextCode + clickCode
});
};
Please always post self-containied examples that can be directly tried in the future.
Now back to your problem: The content script actually has a syntax error.
The following line:
' var pat_torrent = /.torrent$/i ' +
lacks a semicolon, and should be:
' var pat_torrent = /.torrent$/i; ' +
The reason automatic semicolon insertion (ASI) does not work here is: The "code" is actually a string that has no newlines in it whatsoever. If there were newlines, then ASI would have worked.
Anway, another reason not to have complex content script inline. Have a look at contentScriptFile.
This error is actually logged, but the presentation sucks. In the Browser Console:
[20:57:51.707] [object Error] (expandable)
In terminal:
console.error: context-magnet:
Message: SyntaxError: missing ; before statement
Here is a fixed, reproducible sample:
var cm = require("sdk/context-menu");
var contextCode = ' self.on("context", function (node) { '+
' while(node.nodeName!="A") { node = node.parentNode; } '+
' var pat_magnet = /^magnet:/i; ' +
' var pat_torrent = /.torrent$/i; ' +
' if(pat_torrent.test(node.href) || pat_magnet.test(node.href)) { return true; } '+
' else { return false; } '+
' }); ';
cm.Item({
label: "magnet test",
context: cm.SelectorContext("a[href]"),
contentScript: contextCode
});
Edit ' var hash = "" ' has the same problem, and there are might be other such errors that I missed skimming this new code.
As I already said, please use contentScriptFile and not contentScript for long-ish scripts.
Another edit
Here is a builder using contentScriptFile, where I also fixed a couple of other errors, the most important of which are:
Use permissions so that the XHR will work.
Correctly set up the XHR to use responseType and overrideMimeType().
Use onload/onerror instead of onreadystatechange.

How to avoid timeout while reading rows from a query (Phonegap + Javascript)

I am trying to insert around 58000 rows of a query inside a string. But after the row around 8000 I get a timeout error.
I've already tried to use SetTimeout funcions but it was of no use.
Check the code that I am working on:
function onQuerySuccess(tx, results) {
console.log("Entering onQuerySuccess");
if(results.rows) {
console.log("Rows: " + results.rows.length);
var len = results.rows.length;
if(len > 0) {
store_html(results, 0);
console.log("Finished Reading Rows: " + len);
saveNotes();
console.log("Finished Saving Notes");
} else {
//This should never happen
console.log("No rows.");
}
} else {
alert("No records match selection criteria.");
}
console.log("Leaving openView");
function store_html(results, rows_complete){
rows_complete=store_html_input(results, rows_complete);
console.log("Returning row:" + rows_complete);
if (rows_complete<results.rows.length)
{
setTimeout(store_html(results, rows_complete), 50);
}
}
function store_html_input(results, rows_complete){
for(var i = rows_complete; i < rows_complete+100; i++) {
gpsTextFile = gpsTextFile + results.rows.item(i).section + ' ' + results.rows.item(i).timestamp + ' ' + results.rows.item(i).latitude + ' ' +
results.rows.item(i).longitude + ' ' + results.rows.item(i).acx + ' ' + results.rows.item(i).acy + ' ' +
results.rows.item(i).acz + ' ' + results.rows.item(i).speed;
gpsTextFile = gpsTextFile + "\n\r";
}
return i;
}
So.. I get that "Javascript execution exceeded timeout".
Thank you for any of your help!
Best Regards.
You need to change your setTimeout() to NOT execute the function immediately. Change from this:
setTimeout(store_html(results, rows_complete), 50);
to this:
setTimeout(function() {store_html(results, rows_complete)}, 50);
As you had it before, it was immediately executing store_html(results, rows_complete) and passing the return value from that to `setTimeout() which was not delaying anything. This is a common mistake (2nd one of these problems I've answered today).

Categories