Hecatonchires Hecatonchires - 4 months ago 10
jQuery Question

jQuery ajax call only works when I step through it

I am developing a page using Leaflet to display a map for a clients site. The page first loads the details of the map - name, lat, long etc based on an id. The load is via ajax to a very simple c# page that execs a stored proc and returns the recordset as JSON.

Once the map is loaded there is a second ajax call that clears and plots markers on the map. This is to a similar c# page that returns vehicle details. This ajax call is then set into a setInterval(function()) call to repeat every x seconds.

When I run the page normally, nothing loads properly. Looking through the debugger in firefox I can see that the first ajax call has returned undefined. If I add a breakpoint and step through to watch what happens, it works. If I remove the breakpoint, it fails.

What am I doing wrong here?

Main HTML file


<head>
<meta http-equiv="x-ua-compatible" content="IE=11">
<meta charset="utf-8" />
<meta http-equiv="refresh" content="3600">
<title>Map Viewer</title>
<link rel="stylesheet" type="text/css" href="/Content/themes/base/all.css" />
<link rel="stylesheet" type="text/css" href="/Content/Site.css" />
<link rel="stylesheet" type="text/css" href="http://cdn.leafletjs.com/leaflet/v0.7.7/leaflet.css" />

<link rel="apple-touch-icon" sizes="76x76" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16">

<script type="text/javascript" src="/Scripts/jquery-3.0.0.min.js"></script>
<script type="text/javascript" src="/Scripts/jquery-ui-1.11.4.min.js"></script>
<script type="text/javascript" src="http://cdn.leafletjs.com/leaflet/v0.7.7/leaflet.js"></script>
<script type="text/javascript" src="/Scripts/leaflet-providers.js"></script>
<script type="text/javascript" src="/Scripts/leaflet-tracksymbol.js"></script>
<script type="text/javascript" src="/Scripts/leaflet-tracklayer.js"></script>

<script type="text/javascript" src="/Scripts/ctrack-clearybros.js"></script>

<meta name="viewport" content="width=device-width" />
</head>
<body >
<h1 id="pageTitle"></h1>
<div id="errorText"></div>
<div id="map"></div>

<script id="mapScript" type="text/javascript">


Script Block


var mapDetails;
var mapId = 1;
var reloadTime = 15 * 1000; //15 seconds
//var w = $(window).width();
//var h = $(window).height();

mapDetails = getMapDetails(mapId);
console.debug("Map details retrieved");
console.debug(mapDetails);
//$("#map").width(w);
//$("#map").height(h);
$("#pageTitle").text(mapDetails.mapTitle);

var map = new L.Map("map", {
center: mapDetails.mapCenter,
minZoom: 16,
maxZoom: 16,
zoom: 16,
reuseTiles: true,
unloadInvisibleTiles: true
});
console.debug("Map Created");
//console.debug(map);

var appId = "snip";
var appCode = "snip";

//using plugin
var tilesLayer = new L.tileLayer.provider("HERE.terrainDay", {
attribution: "Event Data &copy; <a href=\"http://www.ctrackonline.com.au/\">Ctrack Australia</a> &#151; Map Data &copy; <a href=\"http://www.here.com\">HERE maps</a>",
app_id: appId,
app_code: appCode,
subdomains: '1234',
mapID: 'newest',
base: 'aerial',
maxZoom: 20,
type: 'maptile',
language: 'eng',
format: 'png8',
size: '256'
});
tilesLayer.addTo(map);
console.debug("tileLayer Created");
//console.debug(tilesLayer);

map.removeControl(map.zoomControl);
map.dragging.disable(); //disable map panning
map.doubleClickZoom.disable(); //disable click to recenter
map.touchZoom.disable();
map.scrollWheelZoom.disable();
map.boxZoom.disable();
map.keyboard.disable();
console.debug("Map options set");

var unitLayer = new L.FeatureGroup();
unitLayer.addTo(map);


populateUnitLayers(mapId) //prime the pump before the setInterval fires
console.debug("Vehicle layer populated");
console.debug(unitLayer);
//setInterval(function() {
// populateUnitLayers(mapId) //called every reloadTime seconds
//}, reloadTime);

</script>


<div id="ajaxLoadingHolder">
<div id="ajaxLoading"><img src="/Content/images/ajax-loader.gif" /></div>
</div>


Additional JS file


function getMapDetails(mapId) {
var _mapTitle;
var _mapLatitude;
var _mapLongitude;
var _mapCenter;

var JsonUrl;
if (window.location.pathname == "/ClearyBros.cshtml") { JsonUrl = "/JSON/GetDetailForLocation"; }
if (window.location.pathname == "/CtrackMaps/ClearyBros.cshtml") { JsonUrl = "/CtrackMaps/JSON/GetDetailForLocation"; }

$.ajax({
method: "GET",
url: JsonUrl,
processData: true, //means data sent as querystring
dataType: "json", //,"text"
data: { mapId: mapId },
timeout: 60000
})
.done(function (results) {
if (jQuery.isEmptyObject(results)) {
console.error("Map details are blank");
}
else {
console.debug("Map details are not blank");
console.debug(results);
}
$.each(results, function (index, result) {
_mapTitle = result.Name;
_mapLatitude = result.Latitude;
_mapLongitude = result.Longitude;
_mapCenter = new L.LatLng(_mapLatitude, _mapLongitude);
});
})
.fail(function (xhr, status, error) {
console.error("Failed to load map details");
if (status == "timeout") {
var errorText = "Timeout reached loading map details.";
displayError(errorText);
}
else {
var errortext = "Error state \"" + status + "\" occured loading map details. \n" + error;
displayError(errorText);
}
});

var obj = {
mapTitle: _mapTitle,
mapLatitude: _mapLatitude,
mapLongitude: _mapLongitude,
mapCenter: _mapCenter
};

return obj;
}

function populateUnitLayers(mapId) {
// set all our marker default values here. This doesnt get added to a layer.
var trackSymbolDefault = new L.trackSymbol(new L.LatLng(0.0, 0.0), {
trackId: 0,
fill: true,
fillColor: '#ffffff',
fillOpacity: 1.0,
stroke: true,
color: '#000000',
opacity: 1.0,
weight: 1.0,
speed: 0,
course: 0,
heading: 0,
leaderTime: 0
});

var JsonUrl;
if (window.location.pathname == "/ClearyBros.cshtml") { JsonUrl = "/JSON/GetUnitsForLocation"; }
if (window.location.pathname == "/CtrackMaps/ClearyBros.cshtml") { JsonUrl = "/CtrackMaps/JSON/GetUnitsForLocation"; }

unitLayer.clearLayers();
//unit0Layer.clearLayers();
//unit1Layer.clearLayers();
//unit2Layer.clearLayers();

$("#error-text").hide();

$.ajax({
method: "GET",
url: JsonUrl,
processData: true, //means data sent as querystring
dataType: "json", //"text",
data: { mapId: mapId },
timeout: 60000
})
.done(function (results) {

$.each(results, function (index, result) {
var marker = createUnitMarker(result, trackSymbolDefault);
if (marker.options.speed == 0) {
marker.options.color = '#cccccc';
//marker.addTo(unit0Layer);
//unit0Layer.addTrack(marker);
}
else if (marker.options.speed > 60) {
marker.options.color = '#ff0000';
//marker.addTo(unit1Layer);
//unit1Layer.addTrack(marker);
}
else {
marker.options.color = '#ffff00';
//marker.addTo(unit2Layer);
//unit2Layer.addTrack(marker);
}
//marker.addTo(map);
marker.addTo(unitLayer);
});
})
.fail(function (xhr, status, error) {
console.error("Failed to load vehicle details");
displayError("Error getting data while loading vehicle positions");
if (status == "timeout") {
//alert("Timeout reached.");
displayError("Timeout reached loading map details");
}
else {
//alert("Error state \"" + status + "\" occured loading vehicle positions. \n" + error );
var errorText = "Error state \"" + status + "\" occured loading vehicle positions.";
displayError(errorText);
}
});
}

function createUnitMarker(result, defaults) {
var _marker;
const kphToMph = 0.621371;

var _latlng = new L.LatLng(result.Latitude, result.Longitude);
var _track = result.NodeId;
var _speed = result.Speed * kphToMph; // Km/h to m/h
var _course = result.Heading * Math.PI / 180.0; // Radians from north
var _heading = result.Heading * Math.PI / 180.0;

_marker = new L.trackSymbol(_latlng, {
trackId: _track,
fill: defaults.options.fill,
fillColor: defaults.options.fillColor,
fillOpacity: defaults.options.fillOpacity,
stroke: defaults.options.stroke,
color: defaults.options.color,
opacity: defaults.options.opacity,
weight: defaults.options.weight,
speed: _speed,
course: _course,
heading: _heading,
leaderTime: defaults.options.leaderTime
});

var _note = "<p><b>Unit Name:</b> " + result.UnitName + "<br /><b>Unit Desc:</b> " + result.UnitDesc + "<br /><b>Last Updated:</b> " + result.AssembledTime + "<br /><b>Speed:</b> " + result.Speed + " km/h</p>"
_marker.bindPopup(_note);

console.debug("TrackId " + _marker.options.trackId + " added");

return _marker;
}

function displayError(message) {
$("#error-text").text(message);
$("#error-text").show();
}

$(document).ajaxStart(function () {
$("#ajaxLoading").show();
}).ajaxStop(function () {
$("#ajaxLoading").hide();
});

Answer

I ended up using jQuery's when() and then()

var ajaxOptions = {
    method: "GET",
    url: JsonUrl,
    processData: true, //means data sent as querystring
    dataType: "json", //"text",
    data: { mapId: mapId },
    timeout: 60000,
    cache: false
}

$.when($.ajax(ajaxOptions))
 .then(function (results) {
    $.each(results, function (index, result) {
        // do stuff with each result
    });
});

Putting my ajax in the first calls done did not suit, as the first call happened once on page load, and the 2nd occurred every refresh seconds.