Here is what the console reads: Uncaught TypeError: Cannot read property 'addTo' of undefined
I am currently working with leaflet JS and before the page loads, there is quite a number of Ajax calls going on so that the website can render the acquired data for the user.
I have used two particular methods to synchronize certain ajax calls which rely on the previous. One method is passing the next ajax call into the complete function, whilst another is the when().then() function
The objective with the function below, which is called with the window.onload method, is to determine the users location using the javascript navigator, set the map, and proceed with the ajax calls as mentioned above.
$(window).on('load', function() {
//Pre-loader Jquery
if ($('#preloader').length) {
$('#preloader').delay(200).fadeOut('slow', function() {
$(this).remove();
});
}
//Determine users location, initiate leaflet map, synchornised ajax calls to retreive country core information
navigator.geolocation.getCurrentPosition((success) => {
const crd = success.coords;
onLoadLat = crd.latitude;
onLoadLng = crd.longitude;
currentMap = L.map('map').setView([onLoadLat, onLoadLng], 5);
mapTileLayer.addTo(currentMap);
$.ajax({
url: "libs/php/openCage.php",
type: "POST",
dataType: "JSON",
data: {
latLng: onLoadLat + "+" + onLoadLng
},
success: function(data, textStatus, jqXHRs) {
iso_a2 = data["results"][0]["components"]["ISO_3166-1_alpha-2"];
},
complete: function() {
//Retreive GeoJson file, mark polygon and set initial map for to reflect user location
$.ajax({
url: "libs/php/getCountryBorder.php",
type: "POST",
dataType: "JSON",
data: {
countryCode: iso_a2
},
success: function(geoData, txtSt, jq) {
//Reference details of existing country for further API's and functionality
nameOfCountry = geoData["properties"]["name"];
//Use the index to target the array and create polygon border on map
targetMapData = L.geoJSON(geoData, {
style: borderStyle
});
targetMapData.addTo(currentMap);
currentMap.fitBounds(targetMapData.getBounds(), {
padding: [50, 50],
});
},
complete: function() {
$.ajax({
url: "libs/php/keyCountryInfo.php",
type: "POST",
dataType: "JSON",
data: {
country_code: iso_a2
},
success: function(data, textStatus, jqXHR) {
capitalCity = data["capital"];
currencyCode = data.currencies[0].code;
coordinates = [...data.latlng];
$("#timezoneList").empty();
$("#countryName").text(data["name"]);
$("#region").text(data["region"]);
$("#sub-region").text(data["subregion"]);
$("#countryFlag").attr("src", data["flag"]);
$("#population").text(numeral(data["population"]).format(0, 0));
$("#capital").text(data["capital"]);
$("#timezoneList").text(data["timezones"][0]);
},
complete: function() {
$.when(**getPopularCities()**, getCityWeatherList(), getWikiEntries(onLoadLat, onLoadLng), getGeoNameId(), getCountryImages(), getPublicHolidays(), getLatestExchange(), populateSelect()).
then(function(data, textStatus, jqXHR) {
infoEasyBtn.addTo(currentMap);
wikiEasyBtn.addTo(currentMap);
currencyEasyBtn.addTo(currentMap);
covidEasyBtn.addTo(currentMap);
newsEasyBtn.addTo(currentMap);
weatherEasyBtn.addTo(currentMap);
airportMarkers.addTo(currentMap);
cityMarkers.addTo(currentMap);
siblingMarkers.addTo(currentMap);
});
}
})
}
})
}
})
})
});
within the onload function above, is the getPopularCities() method, which is where the issue is. I have set markers to be shown on the map, but there is one group of markers which do not load, from within that function
Get Popular Cities Method
function getPopularCities() {
let cityPopulation = [];
cityMarkers = new L.MarkerClusterGroup();
if (mainLayer) {
currentMap.removeControl(mainLayer);
}
$.ajax({
url: "libs/php/getCountryCities.php",
type: "POST",
dataType: "JSON",
data: {
getCountryIso: iso_a2
},
success: function(success, textStatus, jqXHRs) {
$("#top10CityTable").empty();
let cityList = success["cities"]; //grab all the cities from the response
//create an array in global variable named cityPopulation, to contain all the markers
cityList.sort((a, b) => {
if (a["population"] > b["population"]) {
return -1
} else {
return 1
}
})
cityList.forEach((element, index) => {
let formatedPop = numeral(element["population"]).format(0, 0);
//depending on how many citys have returned, title the heading accordingly
if (cityList.length < 10) {
$("#top10CityHeading").text("Major Cities")
}
if (cityList.length >= 10) {
$("#top10CityHeading").text("Top 10 Most Populated Cities");
}
//stop index at 9 to get data of top 10 most populated cities for modal section
if (index <= 9) {
$("#top10CityTable").append(`<tr><td>${element["name"]}</td><td>${formatedPop}</td></tr>`)
}
cityMarkers.addLayer(L.marker([element["latitude"], element["longitude"]], {
icon: populatedCities
}).bindPopup(`<h3>${element["name"]}</h3></br>Population: ${formatedPop}`));
})
//create a new overlay prop/value pairing in the global overlays variable
overlays["Major Cities"] = cityMarkers;
},
error: function(text, xh, errorThrown) {
console.log(errorThrown);
},
complete: function() {
let siblingsMapArr = [];
siblingMarkers = new L.MarkerClusterGroup();
$.ajax({
url: "libs/php/getCountrySiblings.php",
type: "POST",
dataType: "JSON",
data: {
countryGeoId: geoNameId
},
success: function(success, textStatus, jqXHRs) {
$("#countrySiblings").empty();
let siblingsArr = success["geonames"];
siblingsArr.sort((a, b) => {
if (a["population"] > b["population"]) {
return -1
} else {
return 1
}
})
let top10Sib = siblingsArr.slice(0, 10);
top10Sib.forEach((element, index) => {
let formatedPop = numeral(element["population"]).format(0, 0);
let rank = index + 1;
siblingMarkers.addLayer(L.marker([element["lat"], element["lng"]], {
icon: citySiblings
}).bindPopup(`<h3>${rank}. ${element["countryName"]}</h3><br/>Population: ${formatedPop}`));
$("#countrySiblings").append(`
<tr>
<td>${element["countryName"]}</td>
<td>${formatedPop}</td>
<td>${element["lat"]} / ${element["lng"]}</td>
</tr>`)
})
overlays["Top 10 Siblings (By Population)"] = siblingMarkers;
},
complete: function(success, data, jq) {
$.ajax({
url: "libs/php/getAirports.php",
type: "POST",
dataType: "JSON",
data: {
countryCode: iso_a2
},
success: function(success, txtStatus, jqXHR) {
let airportsArr = [];
airportMarkers = new L.MarkerClusterGroup();
const airportsList = success["data"];
airportsList.forEach((element) => {
airportMarkers.addLayer(L.marker([element["location"]["latitude"], element["location"]["longitude"]], {
icon: airportIcon
}).bindPopup(`<h3>${element["name"]["original"]}</h3></br>ICAO: ${element["icao"]}</br>Elevation: ${element["elevationFeet"]}<br/>Classification: ${element["classification"]}`));
})
overlays["Airports"] = airportMarkers;
mainLayer = L.control.layers(baseMaps, overlays);
mainLayer.addTo(currentMap);
airportMarkers.addTo(currentMap);
cityMarkers.addTo(currentMap);
siblingMarkers.addTo(currentMap);
}
})
}
})
}
})
}
The markers saved in the variable airportMarkers and cityMarkers appear on the map as well as the leaflet control panel, but the siblings markers do not.
So Im guessing the safe bet is that it is not bringing the data back in time before the page loads? Does anyone have a solution for this or perhaps I have done something wrong in my code?