Appcelerator Titanium - Reverse Geocoder and Variable Scope Issues - javascript

I am trying to use the titanium reverseGeocoder but I am having a strange issue which I think is a "scope" issue. I can't quite understand why the last log call I make returns null values when I have defined the variables in that scope.
var win = Titanium.UI.currentWindow;
Ti.include('includes/db.js');
var city = null;
var country = null;
Titanium.Geolocation.reverseGeocoder( Titanium.UI.currentWindow.latitude,
Titanium.UI.currentWindow.longitude,
function(evt) {
var places = evt.places;
if (places && places.length) {
city = places[0].city;
country = places[0].country;
}
Ti.API.log(city + ', ' + country); // <<< RETURNS CORRECT VALUES
});
Ti.API.log(city + ', ' + country); // <<< RETURNS NULL VALUES

This is an async call as Davin explained. You will have to call a function within the reverse geocode function.
A suggestion I can give you is to work event-based. Create events, and fire events. An example:
Titanium.UI.currentWindow.addEventListener('gotPlace',function(e){
Ti.API.log(e.city); // shows city correctly
});
Titanium.Geolocation.reverseGeocoder( Titanium.UI.currentWindow.latitude,
Titanium.UI.currentWindow.longitude,
function(evt) {
var city, country, places = evt.places;
if (places && places.length) {
city = places[0].city;
country = places[0].country;
}
Ti.API.log(city + ', ' + country); // <<< RETURNS CORRECT VALUES
Titanium.UI.currentWindow.fireEvent('gotPlace',{'city': city, 'country': country});
});

Related

JavaScript: Catch errors in async function and stop the rest of the function if there are any

EDIT: ANSWER BELOW
I'm making my first JavaScript project and decided to make a simple weather app. It fetches weather data of a city you put in from the openweathermap.org api and displays it in a table. I firstly made it using fetch() and .then. I then learned about async functions and the await keyword. After converting the script to an asynchronous function, I came across a problem. If the first city you enter isn't a real city (an error is catched while fetching the api), the warning message appears, BUT the table also appears because the rest of the function still executes.
So my question is: how can I stop the async function if any errors are catched?
Here's the website: https://lorenzo3117.github.io/weather-app/
Here's the code:
// Launch weather() function and catch any errors with the api request and display the warning message if there are any errors
function main() {
weather().catch(error => {
document.querySelector("#warningMessage").style.display = "block";
console.log(error);
});
}
// Main function
async function weather() {
// Take city from input and reset input field
var city = document.querySelector("#cityInput").value;
document.querySelector("#cityInput").value = "";
// Get api response and make it into a Json
const apiResponse = await fetch("https://api.openweathermap.org/data/2.5/weather?q=" + city + "&appid=<apiKey>&units=metric");
const jsonData = await apiResponse.json();
// Removes warning message
document.querySelector("#warningMessage").style.display = "none";
// Puts the Json into an array and launches createTable function
var arrayJson = [jsonData];
createTable(document.querySelector("#table"), arrayJson);
// Function to create the table
function createTable(table, data) {
// Makes the table visible
document.querySelector("#table").style.display = "block";
// Goes through the array and makes the rows for the table
for (let i = 0; i < data.length; i++) {
let rowData = data[i];
var row = table.insertRow(table.rows.length);
// This var exists to make the first letter capitalized without making a gigantic line (see insertCell(3), line 53)
// Could be made into a function if needed
var weatherDescription = rowData.weather[0].description;
// Take latitude and longitude for google maps link
var lat = rowData.coord.lat;
var long = rowData.coord.lon;
// Make an a-tag for link to google maps
var mapLink = document.createElement("a");
mapLink.innerHTML = "Link";
mapLink.target = "_blank";
mapLink.href = "https://www.google.com/maps/search/?api=1&query=" + lat + "," + long;
// Making rows in table
row.insertCell(0).innerHTML = rowData.name + ", " + rowData.sys.country;
row.insertCell(1).innerHTML = rowData.main.temp + " °C";
row.insertCell(2).innerHTML = rowData.main.humidity + "%";
row.insertCell(3).innerHTML = weatherDescription.charAt(0).toUpperCase() + weatherDescription.slice(1);
row.insertCell(4).appendChild(mapLink); // appendChild for anchor tag because innerHTML only works with text
}
}
And the repo: https://github.com/lorenzo3117/weather-app
Thank you
you can do this :
async function weather() {
try {
const apiResponse = await fetch("https://api.openweathermap.org/data/2.5/weather?q=" + city + "&appid=02587cc48685af80ea225c1601e4f792&units=metric");
} catch(err) {
alert(err); // TypeError: failed to fetch
return;
}
}
weather();
Actually, the error catched isn't an error with the api itself because the api still sends a json, but the error is catched while trying to read a certain object from the json (which doesn't exist because the json isn't a normal one with weather data). Therefore the function stops far later than expected, after the table was made visible.
I just put the line that made the table visible after the function that creates the table (after where the real error occurs). Also thanks #Dadboz for the try catch method which made the code even more compact. I also added an if else to check if the json file is the correct one so unnecessary code doesn't get executed. Thanks #James for pointing this out to me.
Here's the final code:
// Main function
async function weather() {
try {
// Take city from input and reset input field
var city = document.querySelector("#cityInput").value;
document.querySelector("#cityInput").value = "";
// Get api response and make it into a Json
const apiResponse = await fetch("https://api.openweathermap.org/data/2.5/weather?q=" + city + "&appid=<apiKey>&units=metric");
const jsonData = await apiResponse.json();
if (jsonData.message == "city not found") {
document.querySelector("#warningMessage").style.display = "block";
} else {
// Removes warning message
document.querySelector("#warningMessage").style.display = "none";
// Puts the Json into an array and launches updateTable function
var arrayJson = [jsonData];
updateTable(document.querySelector("#table"), arrayJson);
}
}
catch (error) {
console.log(error);
}
}
// Function to update the table
function updateTable(table, data) {
// Goes through the array and makes the rows for the table
for (let i = 0; i < data.length; i++) {
let rowData = data[i];
var row = table.insertRow(table.rows.length);
// This var exists to make the first letter capitalized without making a gigantic line (see insertCell(3), line 53)
// Could be made into a function if needed
var weatherDescription = rowData.weather[0].description;
// Take latitude and longitude for google maps link
var lat = rowData.coord.lat;
var long = rowData.coord.lon;
// Make an a-tag for link to google maps
var mapLink = document.createElement("a");
mapLink.innerHTML = "Link";
mapLink.target = "_blank";
mapLink.href = "https://www.google.com/maps/search/?api=1&query=" + lat + "," + long;
// Making rows in table
row.insertCell(0).innerHTML = rowData.name + ", " + rowData.sys.country;
row.insertCell(1).innerHTML = rowData.main.temp + " °C";
row.insertCell(2).innerHTML = rowData.main.humidity + "%";
row.insertCell(3).innerHTML = weatherDescription.charAt(0).toUpperCase() + weatherDescription.slice(1);
row.insertCell(4).appendChild(mapLink); // appendChild for anchor tag because innerHTML only works with text
}
// Makes the table visible
document.querySelector("#table").style.display = "block";
}
Thanks everyone for your answers, have a good day!
Lorenzo

JavaScript Class Objects not returning value

I've been working with the Microsoft Bot Framework to create a bot that can interface between MS Teams and AWS. I've been trying to write some JS functions but have been unsuccessful in getting them to operate how I want them to.
Here is what I am currently working on and am stuck on:
I am creating a 'ping' like functionality so a bot user can ping an instance in AWS and receive its status whether its running and has passed the system checks or not. My code is currently able to take the user request for the ping, retrieve the information from AWS, and can even print that info to the console. However, when I am trying to retrieve that information back out of the object that I set it to and print it to MS Teams, it says my variable is undefined.
Some code snippets are below:
class aws_Link {
constructor (mT, ping_1, i_state, i_status) {
this.myTag = mT;
this.ping = ping_1;
this.instance_state = i_state; // I declare this here, but should I?
this.instance_status = i_status; // I declare this here, but should I?
}
//i_state and i_status are just passed NULL when the object is initialized
//so they would be holding some value, not sure if I have to do this
api_link () {
var mainLink = API_LINK_TAKEN_OUT_FOR_OBVIOUS_REASONS;
var myTagFill = "myTag=";
var ampersand = "&";
var pingFill = "ping=";
var completeLink = String(mainLink + myTagFill + this.myTag + ampersand + pingFill + this.ping);
var finalLink = completeLink;
finalLink = finalLink.split(' ').join('');
//set up API-key authenticication
var options = {
url: finalLink,
headers: {
'x-api-key': 'AWS-PRIVATE-TOKEN'
}
};
if(this.ping == "TRUE") { // if the user wants to use /ping
var res = request(options, function(error, response, body) {
console.log("PING REQUEST"); //debug
body = JSON.parse(body);
var h_state = body['instanceState'];
var h_status = body['instanceStatus'];
this.instance_state = h_state;
this.instance_status = h_status;
console.log("STATE: " + h_state); //debug
console.log("STATUS: " + h_status); //debug
});
}
}
pingFunction () {
var tmp = "Instance State: " + this.instance_state + " Instance Status: " + this.instance_status;
return tmp;
}
}
And here is where I call the api_link() function and pingFunction():
var apiLink1 = new aws_Link("MY_TAG_VALUE", "TRUE", "NULL", "NULL");
var completeAPILink = apiLink1.api_link();
session.send('Request complete.');
session.send("PING: " + apiLink1.pingFunction());
So essentially the user enters in some info which gets passed to where I create the "new aws_Link" which then a my understanding is, creates an object called apiLink1. From there, it makes the request to AWS in my api_link() function, which retrieves the info I want. I thought I was then saving this info when I do the: this.instance_state = h_state; & this.instance_status = h_status;. So then when I call pingFunction() again on apiLink1, I thought I would be able to retrieve the information back out using this.instance_state and this.instance_status, but all it prints out is undefined. Any clarification on why my current code isn't working and any changes or improvements I can make would be greatly appreciated.
Thanks!

listbox does not pass string to google script

When I select a user from listbox, the onChange() event triggers a function. It should pass a string to the function. Then the code finds the user's password and returns it for comparison. The following is the code which works fine if I hard code the user value, but not when I select it from the listbox.
function addClients(clients){
$('#customer').empty();
$('#customer').append('<option> ---- Choose a user ----</option>');
for (var i in clients) {
$('#customer').append('<option>'+clients[i]+'</option>');
$('#customer').trigger("chosen:updated");
}
}
getval function:
function getval(sel){
var usrpass = google.script.run.getuserpass(sel.value);
alert(usrpass);
}
the function in code.gs is as follows
function getuserpass(userval){
var usrpass = "";
var doc = SpreadsheetApp.openById("spreadsheet id");
var sheet = doc.getActiveSheet();
var data = sheet.getRange(3, 3, sheet.getLastRow(),5).getValues();;
for(n=0;n<data.length;++n){
// iterate row by row and examine data in column A
if(data[n][0].toString().match(userval)==userval){ usrpass = data[n][4]};
}
return usrpass;
}
Why does the return value come back as undefined rather than the password.
If I hardcode username in the function and run the function, then the return value is the value in the fifth column.
Try structuring the code like this:
<script>
function onSuccess(returnVal) {
alert('Success! ' + returnVal);
};
function getval(sel){
var selectValue = sel.value;
console.log('selectValue: ' + selectValue);
google.script.run
.withSuccessHandler(onSuccess)
.getuserpass(sel.value);
};
</script>
You can iterate through the object to see what is really in it, as a debugging test.
for (var propertyVal in sel) {
console.log('this property: ' + propertyVal);
console.log('this value: ' + sel[propertyVal]);
};
And see what is really in the object.

How to call inner function in Openlayer?

I have trouble about selecting the feature layer based on its attribute. I got this code below but it says:
Uncaught TypeError: Cannot read property 'features' of undefined
here's my code:
var init = function () { // A function that will initialize and execute all the declared variables
var geographic = new OpenLayers.Projection("EPSG:4326"); // Setting the standard geographic projection
var mercator = new OpenLayers.Projection("EPSG:3857"); // Setting the universal geographic projection
map = new OpenLayers.Map('map'); // Creating & initializing map constructor
var base_osm = new OpenLayers.Layer.OSM("OpenStreetMap"); // Setting OpenStreetMap as a BaseMap
map.addControl(
new OpenLayers.Control.MousePosition({
prefix: '<small style="color:blue">',
suffix: '</small>',
numDigits: 2,
emptyString: '<small style="color:red">' + 'Mouse is not over map.' +'</small>'
})
);
var layer_agao = new OpenLayers.Layer.Vector("Agao");
map.addLayers([layer_agao, base_osm]); // Adding the vector layer to the map
map.addControl(new OpenLayers.Control.LayerSwitcher());
selectControl = new OpenLayers.Control.SelectFeature(layer_agao, {
onSelect: onFeatureSelect, onUnselect: onFeatureUnselect
});
map.addControl(selectControl);
selectControl.activate();
map.setCenter(new OpenLayers.LonLat(13975400.3513, 999830.692078),16);
var format_agao = new OpenLayers.Format.GeoJSON(); //initializing and calling the rendered GeoJSON Layer from views.py
var feat_agao = format_agao.read({{agao_transform|safe}});
layer_agao.addFeatures(feat_agao);
layer_agao.events.on({
featureselected: function(event) {
var feature = event.feature;
var area = feature.geometry.getArea();
var id = feature.attributes.newpin;
var output = "Land Pin: " + id + "<br/>" + "Area: " + area.toFixed(12);
document.getElementById("status").innerHTML = output;
}
});
init.showparcel();
}
init.showparcel = function (getpin){
for(var f=0;f<layer_agao.features.length;f++) {
if(layer_agao.features[f].attributes.newpin == getpin) {
selectControl.select(layer_agao.features[f]);
break;
}
}
}
I also read about getfeaturesbyattribute, but i can't find any example. So, is there other way to call the specific feature layer on click (event)? This is for my searching...
You would need to use the getFeaturesByAttribute or track features in your own index with their FID as the index of that object, and then use getFeatureByFid.
I usually prefer to track them in my own object or hashtable and then reference by FID.
In your example I would pull in an unique id on the attribs that you can search yourself outside of openlayers, and then use the getFeaturesByAttribute to reference the unique id that you know exist. If that doesn't may sense hit me up in the comments.
vlayer.getFeaturesByAttribute("fid", target)[0]
http://dev.openlayers.org/docs/files/OpenLayers/Layer/Vector-js.html#OpenLayers.Layer.Vector.getFeaturesByAttribute
The correct way to add a listener to a Vector.Layer is layer.events.register(type, obj, listener) as shown in the comments in the source: http://trac.osgeo.org/openlayers/browser/trunk/openlayers/lib/OpenLayers/Layer/Vector.js. Note, the listener for featureselected is passed the selected feature, not the event as you have it.
So, in your case:
layer_agao.events.on('featureselected', null, function(feature){
//do something with the feature
var area = feature.geometry.getArea();
var id = feature.attributes.newpin;
var output = "Land Pin: " + id + "<br/>" + "Area: " + area.toFixed(12);
document.getElementById("status").innerHTML = output;
});
getFeaturesByAttribute doesn't look like it is what you need, based on your code sample, though it is useful in specific cases.

How to add computed observable to knockoutjs mapping

I'm using the mapping plugin to create my client side view model based on an object that gets sent from the server. The object is basic address information ie: address1, address2, city, state, postal, ect...
Once the view model is bound, I want a google maps canvas to update if the user changes the address. I created a computed observable that checks the values entered and calls a map update function. I had this working before when I wasn't using the mapping plugin, ie model was defined locally, but once I introduced mapping I wasn't able to append the computed observable to the view model.
I tried following the instructions from the mapping plugin documentation, but the computed observable isn't triggering updates. I have a custom mapping that calls a mapModel which contains the computed observable as in the examples, but no updates.
Any ideas?
$.getJSON("#Url.RouteUrl("
ContactUs_default ", new { action = "
GetPageModel ", Model.BusinessID})", function(result) {
//create map property
result.Data.Map = null;
var mapping = {
'Map': {
create: function(options) {
return new mapModel(options.data);
}
}
};
var viewModel = ko.mapping.fromJS(result.Data, mapping);
ko.applyBindings(viewModel);
});
var mapModel = function(data) {
ko.mapping.fromJS(data, {}, this);
this.Map = ko.computed(function() {
var address = "";
var enteredElements = 0;
if (this.Address1 != helpText) {
address += " " + this.Address1;
enteredElements++;
}
if (this.Address2 != helpText) {
address += " " + this.Address2;
}
if (this.City != helpText) {
address += " " + this.City;
enteredElements++;
}
if (this.State != helpText) {
address += " " + this.County;
enteredElements++;
}
if (this.PostalCode != helpText) {
address += " " + this.Postal;
}
alert("hi");
//only upate map if enough data has been entered to give accruate location
if (enteredElements >= 3) {
MYMAP.placeMarkers(address);
}
}, this);
};​
When you send your data through the mapping plugin, all of your properties will become observables.
This means that you need to access them as a function like like:
if (this.Address1() != helpText) {
address += " " + this.Address1();
enteredElements++;
}
When you access them as observables inside of a computed observables, then it will create a dependency. So, currently your computed observable would get initially evaluated, but it would never update again, as it does not access the value of any observables.

Categories