I'm working through the process of modulization on an app that I have written. This works with spatial location
I'm using an event to query for the user's lat / lon position for use inside the application. My calling snippet is below (button click starts it up)
<script>
require([
'dojo/dom',
'dojo/_base/array',
'demo/testModule',
'esri/SpatialReference',
'esri/geometry/Point'
], function (
dom,
arrayUtils,
testModule,
SpatialReference,
Point
) {
//Here is the button click listener
$('#whereAmIButton').click(function () {
var spatialRef = new esri.SpatialReference({ 'wkid': 4326 });
//variable I want to set to a returned geometry.
var myGeom;
//This runs but I'm missing the boat on the return of a value
testModule.findUserLocPT(spatialRef);
//var myModule = new testModule(); //not a constructor
});
});
</script>
Here is the custom module. It logs the information to the console for the user's location. But I want to return the value for setting the 'myGeom' variable.
define(['dojo/_base/declare','dojo/_base/lang','dojo/dom',
'esri/geometry/Point','esri/SpatialReference'], function (
declare, lang, dom, Point, SpatialReference) {
return {
findUserLocPT: function (spatialRef) {
var geom;
var location_timeout = setTimeout("geolocFail()", 5000);
navigator.geolocation.getCurrentPosition(function (position) {
clearTimeout(location_timeout);
var lat = position.coords.latitude;
var lon = position.coords.longitude;
setTimeout(function () {
geom = new Point(lon, lat, spatialRef);
//console.log writes out the geom but that isnt what I am after
console.log(geom);
//I want to return this value
return geom;
}, 500);
});
function geolocFail() {
console.log("GeoLocation Failure");
}
}
}//end of the return
});
Any help would be welcome. I can by reference back change textual/html values on the document but am not getting things back as a variable.
Andy
Ok, I don't know if this is the 'best' answer but I have one now.
I added a global variable inside the 'test.html' page
<script>
var theGeom; //This is the variable
require([
'dojo/dom',
here is where I am setting the value of this variable for use in the original dojo 'require' code block. This is coming from the 'testModule.js'
setTimeout(function () {
geom = new Point(lon, lat, spatialRef);
theGeom = geom; //Here is the feedback of the value to the global variable.
return myGeom;
}, 500);
$('#whereAmIButton').click(function () {
var spatialRef = new esri.SpatialReference({'wkid':4326});
testModule.findUserLocPT(spatialRef);
setTimeout(function () {
console.log(theGeom); //here is the value set and ready to use
},2000);
});
I'm not sure if this is the best way. If you have something better please let me know.
Andy
Related
FYI Trial 1 does not work, but Trial 2 works.
I understand that getJSON is executes asynchronously, but I don't actually understand how it applies to the code I've written.
What can I learn about asynchronous execution from this?
Why do I have to separate the getJSON call into a function separate from ready()?
FOR TRIAL 2:
How can I write this code so I don't have to initialize functions inside of getJSON? If there isn't a way, how can I write this code to be more robust?
/*
//TRIAL 1
$(document).ready(function(){
console.log("loaded");
$.getJSON(url, function(json){
var fahrenheit = true;
getLocation(json);
getTemperature(fahrenheit, json);
$("#unit").on("click", function(){
fahrenheit = !fahrenheit;
getTemperature(fahrenheit, json);
});
getWeather(json);
});
});
//Gets current weather conditions from current_observation
function getWeather(json){
var currWeather = "";
var iconURL = "";
currWeather=json.current_observation.weather;
iconURL=json.current_observation.icon_url;
$("#icon").attr("src", iconURL);
$("#weather").html(currWeather);
};
//Gets current temperature from current_observation
function getTemperature(fahrenheit, json){
var currTemp = 0;
if(fahrenheit){
currTemp+=json.current_observation.temp_f;
currTemp+="℉";
} else{
currTemp+=json.current_observation.temp_c;
currTemp+="℃";
}
$("#temperature").html(currTemp);
};
//Gets city, state, country, zip, latitude, and longitude from location
function getLocation(json){
var currLocation=["city", "state", "country", "zip", "lat", "lon"];
var locationHTML = "";
currLocation[0] = json.location.city;
currLocation[1] = json.location.state;
currLocation[2] = json.location.country_name;
currLocation[3] = json.location.zip;
currLocation[4] = json.location.lat;
currLocation[5] = json.location.lon;
locationHTML += currLocation[0]+", "+currLocation[1]+", "+currLocation[2]+" " +currLocation[3]+"<br>";
locationHTML += "Latitude: "+currLocation[4]+"<br>Longitude: "+currLocation[5];
$("#location").html(locationHTML);
};
*/
//TRIAL 2
$(document).ready(function(){
console.log("loaded");
dispWeather();
});
function dispWeather(){
console.log("inside dispWeather");
//Retrieve json from weather underground
var url = "https://api.wunderground.com/api/19c5c96f0b140c0f/geolookup/conditions/q/autoip.json";
$.getJSON(url, function(json){
console.log("Got JSON");
console.log(json);
var fahrenheit = true;
getLocation(json);
getTemperature(fahrenheit, json);
$("#unit").on("click", function(){
fahrenheit = !fahrenheit;
getTemperature(fahrenheit, json);
});
getWeather(json);
//Gets current weather conditions from current_observation
function getWeather(json){
var currWeather = "";
var iconURL = "";
currWeather=json.current_observation.weather;
iconURL=json.current_observation.icon_url;
$("#icon").attr("src", iconURL);
$("#weather").html(currWeather);
};
//Gets current temperature from current_observation
function getTemperature(fahrenheit, json){
var currTemp = 0;
if(fahrenheit){
currTemp+=json.current_observation.temp_f;
currTemp+="℉";
} else{
currTemp+=json.current_observation.temp_c;
currTemp+="℃";
}
$("#temperature").html(currTemp);
};
//Gets city, state, country, zip, latitude, and longitude from location
function getLocation(json){
var currLocation=["city", "state", "country", "zip", "lat", "lon"];
var locationHTML = "";
currLocation[0] = json.location.city;
currLocation[1] = json.location.state;
currLocation[2] = json.location.country_name;
currLocation[3] = json.location.zip;
currLocation[4] = json.location.lat;
currLocation[5] = json.location.lon;
locationHTML += currLocation[0]+", "+currLocation[1]+", "+currLocation[2]+" " +currLocation[3]+"<br>";
locationHTML += "Latitude: "+currLocation[4]+"<br>Longitude: "+currLocation[5];
$("#location").html(locationHTML);
};
})
};
.ready() jQuery Documentation
Specify a function to execute when the DOM is fully loaded.
What can I learn about asynchronous execution from this?
Your learning that you don't know when the document is going to be ready() so we wait until the event completes before beginning execution on our application. You also learned that you have to wait for $.getJSON to fetch json then you process the data.
Why do I have to separate the getJSON call into a function separate from ready()?
As specified above .ready() is waiting for the DOM to be fully loaded, then we start the application. So when we are "ready" lets fetch the weather data. The document is only ready one time when the DOM is fully loaded.
How can I write this code so I don't have to initialize functions inside of getJSON?
Without you being specific, I'm assuming your problem here was with toggling the degrees between celsius and fahrenheit. After we load the weather you can store the data in a variable outside of the scope of the function, this way when you click to change the degrees you can pass in the same data without having to call the api again (although at this point the weather could have changed)
how can I write this code to be more robust?
I've included a JS Bin that alters your code. The biggest problem was bad naming conventions and not keeping things simple. Example getWeather() was not "getting weather" it was setting html from data we got from $.getJSON which was invoked in your ready() instead of breaking it out into another function we could call later on.
For the most part this is how the code reads now, clear function names help quickly see what this code is supposed to do.
$(document).ready(function() {
renderWeather();
});
var state = {
fahrenheit: true,
data: {}
};
function renderWeather() {
getWeatherJSON()
.then(data => {
state.data = data;
setWeatherHTML(data);
setTemperatureHTML(data, state.fahrenheit);
setLocationHTML(data);
})
.catch(err => console.log(err));
}
http://jsbin.com/cekite/edit?js,output
If we wanted to take this a step further we could create a WeatherAPI prototype concealing our html render functions and extend it with a WeatherUndergroudAPI prototype, this way if we ever change our weather service we should only have to implement a format function to marshall the data the way the WeatherAPI expects it to be in.
class WeatherAPI {
constructor(opt) {
...
}
init() {
...
}
get() {
... feteches json from endpoint provided
}
renderWeather() {
this.get()
.then(this.formatter.bind(this))
.then(this.setWeatherData.bind(this))
.then(this.renderWeatherHTML.bind(this))
.then(this.renderTemperatureHTML.bind(this))
.then(this.renderLocationHTML.bind(this))
.catch(err => console.log(err));
}
formatter(data) {
...
}
setWeatherData(data) {
...
}
renderWeatherHTML() {
...
}
renderTemperatureHTML() {
...
}
renderLocationHTML() {
...
}
}
Extending the WeatherAPI is then a matter of passing in a new endpoint to get data from. Or in this case overriding the WeatherAPI get method and returning static data.
class FakeWeatherAPI extends WeatherAPI {
constructor(opt = {}) {
super(opt);
}
get() {
return new Promise((resolve) => {
const data = {
someReallyWeirdKeyForACity: 'San Francisco',
state: 'CA',
country: 'US',
lat: '37.77999878',
lon: '122.41999817',
f: '1000',
c: '25',
icon: 'http://www.nyan.cat/cats/zombie.gif',
weather: 'Acid Rain'
};
resolve(data);
});
}
formatter(data) {
const formattedData = {
city: data.someReallyWeirdKeyForACity,
state: data.state,
country: data.country,
lat: data.lat,
lon: data.lon,
temperature: {
fahrenheit: data.f,
celsius: data.c
},
icon: data.icon,
weather: data.weather
};
return formattedData;
}
}
Our application init code then becomes.
$(document).ready(init);
function init(){
const weatherAwesomeService = new FakeWeatherAPI();
weatherAwesomeService.init();
};
Here is a working jsbin for they above
http://jsbin.com/sicofe/edit?js,output
I have a function in my processing.js file that calls for an AJAX script to return an array with which I want to define some simple starting variables for my code. I want this function to complete before any other part of the code that comes after it is executed.
When i run my code both the alerts in the setup() phase and the first alert in the draw() return "undefined" while the next iterations in the draw() phase return the actual array I need.
The processing code:
var loadArray;
setup() {
loadArray = new Array();
js_GetArray(function(data) {
loadArray = data;
});
alert(loadArray[1]);
alert(loadArray[2]);
}
void draw()
{
alert(loadArray[k]);
k++;
}
The AJAX:
<!-- Read the vars numbers from the mysql database and return them -->
<script language="JavaScript" type="text/javascript">
function js_GetArray(callback) {
$.get( "load.php", function( actiondata ) {
var obj = JSON.parse(actiondata);
callback(obj);
});
}
</script>
Is there a way to force the program to complete the js_GetArray call before moving on to the rest of the code?
If it is during loading of the page, then what about using a flag as indication that the call has returned?
var loadArray;
var isloaded = false;
setup() {
loadArray = new Array();
js_GetArray(function(data) {
loadArray = data;
isloaded = true;
alert(loadArray[1]);
alert(loadArray[2]);
});
}
void draw()
{
if(isloaded)
{
alert(loadArray[k]);
k++;
}
}
EDIT:
I have moved the alerts inside the callback function.
As an alternative answer to the one given, I've covered this on the processingjs.org website extensively, so you probably want to give that a read: http://processingjs.org/articles/PomaxGuide.html#interface
I'm stuck at this issue where I can't seem to assign a new value to the created object variable. See below:
// Vesselposition class
function vessel(name,ajaxName,dataUrl,pointLimit,polylineColor,iconUrl) {
this.name = name;
this.ajaxName = ajaxName;
this.dataUrl = dataUrl;
this.pointLimit = pointLimit;
this.polylineColor = polylineColor;
this.iconUrl = iconUrl;
// Global variables
this.lat=0;
this.lng=0;
this.latlng;
this.dateTime, this.vesselIcon, this.marker, this.polyline, this.localTemp, this.localWindSpeed, this.localWindDir;
this.countryName, this.countryCode, this.localTime, this.localSunrise, this.localSunset, this.countryFlag;
this.localTemp, this.localWindSpeed, this.localWindDir, this.myOptions, this.ib;
// Function gets position data
this.getData = function() {
$.when(
$.getJSON(this.dataUrl, { vessel: this.ajaxName, limit: this.pointLimit })
).done(function (data){
this.path = [];
// Create vessel icon for marker
this.vesselIcon = new google.maps.MarkerImage(this.iconUrl,
// This marker is 60 pixels wide by 58 pixels tall.
new google.maps.Size(60, 58),
// The origin for this image is 0,0.
new google.maps.Point(0,0),
// The anchor for this image is centered at 30,29 pixels.
new google.maps.Point(30, 29)
);
if (data.markers.length < 1) {
document.getElementById("map_canvas").innerHTML = "<h2>There was a problem obtaining vessel data, wait a couple of minutes and refresh your browser!</h2>";
} else {
for(i=0;i<data.markers.length;i++) {
// Assign lat,lng, id, dateTime and heading
this.lat = data.markers[i].marker.lat;
this.lng = data.markers[i].marker.lng;
What I want to accomplish is to assign this.lat and this.lng the coordinate values inside the for-loop. Later on, those values should be passed on to the getData method.
Please help guys! Been stuck on this for 3 hours searching the web!
Try this:
//keep a reference to this
var self = this;
// Function gets position data
this.getData = function() {
....
//use self here
self.lat=
I found a solution. By using jQuery's proxy function.. I got it to work.
// Function gets position data
this.getData = function() {
$.when(
$.getJSON(this.dataUrl, { vessel: this.ajaxName, limit: this.pointLimit })
).done($.proxy(function (data){
this.path = [];
),this}
Using the same class variables as first provided.. the key is to use $.proxy method in the .done function with the scope "this".
/**
* Downloads the fingerprint preview data
*/
this.fetchFingerprintPreviews = function (url) {
var that = this;
var dfd = jQuery.Deferred();
jQuery.get(url)
.done(function (resp) {
var linkNodes = conn.getLinksViaRelation(resp,
'http://ws.bdr.de/webhd/hdcap/rels/finger-preview/');
jQuery(linkNodes).each(function () {
var link = jQuery(this);
var fpIndex = link.prev("index, bdr\\:index").html();
var fpType = link.attr('type');
jQuery.get(link.attr('href'), {"encoding":"base64"}, null, "text")
.done(function (imageDataBase64) {
fingerprintPreview[fpIndex] = {};
fingerprintPreview[fpIndex].imageData = imageDataBase64;
fingerprintPreview[fpIndex].type = fpType;
console.log(fingerprintPreview);
if (Object.keys(fingerprintPreview).length ==
Object.keys(linkNodes).length) {
dfd.resolve();
}
});
});
});
return dfd;
}
a new version added which makes use of lexical local variables. still not working.
m a bit lost at the moment...
also added a log statement which gets called only one time.
i would expect the log to get called two times.
any ideas?
The problem isn't that your done callback is called once, but that you change the same fingerprintPreview each time it is called because that has the value of end of loop when the callbaks are called.
The solution is to not reuse this externally declared variable but a new one, declared in the function you give to each :
jQuery(linkNodes).each(function () {
var link = jQuery(this);
var fpIndex = link.prev("index, bdr\\:index").html();
var fpType = link.attr('type');
jQuery.get(link.attr('href'), {"encoding":"base64"}, null, "text")
.done(function (imageDataBase64) {
fingerprintPreview[fpIndex] = {};
fingerprintPreview[fpIndex].imageData = imageDataBase64;
fingerprintPreview[fpIndex].type = fpType;
if (Object.keys(fingerprintPreview).length == Object.keys(linkNodes).length) {
alert("foo");
}
});
});
i found out what the problem was. dystroys answer is correct but was not adressing my original problem. so the thing is that when i have a firebug breakpoint in my done function callback it will be called only one time. so having breakpoints set with firebug can lead to uninterpreted js code... ffs!
I am reading a Rss feed using setInterval method and displaying notification to the users ,I want to make sure I store the latest feed title so that the user does not get multiple notification of the same title again. The current implementation does not work because I cant use that variable until the response comes back. To make things worse I am delaying the execution.So I am guessing I need to use callback function get the value and do my checking inside that function. I am not able to figure out how to do the callback and get the value of entry_title.
/** global variable **/
var global_Rsstitle;
/** end global variable **/
function get_rss1_feeds() {
var Rss1_title = getRss("http://rss.cnn.com/rss/cnn_topstories.rss", function(entry_title) {
if(global_Rsstitle != entry_title)
global_Rsstitle = entry_title;
console.log('test',global_Rsstitle); // the value is outputed but global var is not working
});
console.log('test1',global_Rsstitle); // outputted as undefined ??
}
google.load("feeds", "1");
google.setOnLoadCallback(function () { setInterval(get_rss1_feeds, 5000); });
My jsRss.js
function getRss(url, callback){
if(url == null) return false;
// Our callback function, for when a feed is loaded.
function feedLoaded(result) {
if (!result.error) {
var entry = result.feed.entries[0];
var entry_title = entry.title; // need to get this value
callback && callback(entry_title);
}
}
function Load() {
// Create a feed instance that will grab feed.
var feed = new google.feeds.Feed(url);
// Calling load sends the request off. It requires a callback function.
feed.load(feedLoaded);
}
Load();
}
can u see the entry_title -> this stores d value i need
so i need to get this value n store it into a global variable
or send it to another fns as a argument
so that I can maintain the value
and when next time setInterval is fired
I get a new value so I can compare and check if its same
n if its same I dont display it to the user
google.load("feeds", "1");
google.setOnLoadCallback(function () {
var oldTitle = '',
newTitle = '',
getRss = function (url, callback) {
(url) && (function (url) {
var feed = new google.feeds.Feed(url);
feed.load(function (result) {
(!result.error && callback) && (callback(result.feed.entries[0].title));
});
}(url));
};
setInterval(function () {
getRss(
'http://rss.cnn.com/rss/cnn_topstories.rss',
function (title) {
newTitle = title;
if(oldTitle !== newTitle) {
oldTitle = newTitle;
console.log('oldTitle: ', oldTitle);
}
console.log('newTitle: ', newTitle);
}
);
}, 5000);
});