I'm working on a project where I have a json file that contains a list of companies, their phone numbers, addresses, etc. Currently I have it loading a list into a table with no real order. It works to list the companies, and displays their location on a map with a pin. I have it limited to display on 10 as its almost 5000 entries - at least until I get a sort/search function working. The issue I'm having is that the only way I can get the map to load and the pins to work is if I include them as part of the $.getJSON. If I create functions to load outside of this, I'm unable to call the information out of the JSON any longer, but likewise am not able to include the functions inside as they don't work.
Here is the code I'm working with:
$.getJSON('MapDatabroke.json', function (data) {
var output = "<table class = sample>";
tableHeadings = "<thead>" +
"<tr>" +
"<th></th>" +
"<th><u>Name:</u></th>" +
"<th><u>Address:</u></th>" +
"<th><u>City:</u></th>" +
"<th><u>State:</u></th>" +
"<th><u>Phone Number:</u></th>" +
"<th><u>PO:</u></th>" +
"<th><u>Towing:</u></th>" +
"<th><u>Tires:</u></th>" +
"<th><u>MC:</u></th>" +
"<th><u>RoadSvc:</u></th>" +
"<th><u>Notes:</u></th>" +
"</tr>" +
"</thead>";
output += tableHeadings;
for (var i = 0; i < length; i++) {
var lat = data[i]["Lat"];
var lon = data[i]["Lon"];
var dist = 41.5;
if (lat < dist) {
output += "<tr>";
if (data[i]["Website"] == '---') {
output += "<td>" + ' ' + "</td>";
} else {
output += "<td>W</td>";
}
output += "<td>" + i + data[i]["Business Name"] + "</td>" + "<td>" + data[i]["Address"] + "</td>" + "<td>" + data[i]["City"] + "</td>" + "<td>" + data[i]["StateListing"] + "</td>" + "<td>" + data[i]["Phone"] + "</td>";
if (data[i]["PO"] == 'FALSE') {
output += "<td>" + data[i]["Notes"] + "</td>";
output += "</tr>";
}
}
output += "</table>";
document.getElementById("placeholder").innerHTML = output;
});
function GetMap() {
var map = null;
var pinInfobox = null;
// Initialize the map
var map = new Microsoft.Maps.Map(document.getElementById("mapDiv"), {
credentials: "AqtpRoPv2sfmrgf9VhyvDV8hCOVGPJi0-9heYhxmB-WU24OzpTIOIR0-C4fD0jc-",
center: new Microsoft.Maps.Location(45.5, -122.5),
zoom: 7
});
// Creates a collection to store multiple pins
var pins = new Microsoft.Maps.EntityCollection();
// Create Pins
for (var i = 0; i < length; i++) {
//pushpin location
var position = new Microsoft.Maps.Location(data[i]["Lat"], data[i]["Lon"]);
//Create the pin
var pin = new Microsoft.Maps.Pushpin(position);
//add pin to collection
pins.push(pin);
}
//add pins
map.entities.push(pins);
//create the info box
pinInfobox = new Microsoft.Maps.Infobox(pin.getLocation(), {
title: 'My Pushpin',
description: 'It works!',
visible: false,
offset: new Microsoft.Maps.Point(0, 15)
});
//add click event
Microsoft.Maps.Events.addHandler(pin, 'click', displayInfobox);
//hide box on map move
Microsoft.Maps.Events.addHandler(map, 'viewchange', hideInfobox);
}
function displayInfobox(e) {
pinInfobox.setOptions({
visible: true
});
}
function hideInfobox(e) {
pinInfobox.setOptions({
visible: false
});
}
The way it shows above, doesn't work. It will load the map, but will not load the information as well as give me a error of data not defined w/in the create pin. Any ideas how to assign the information so that way I can use it in functions later on?
Try asigning a variable to the $.getJSON (var JSONData = $.getJSON...). In that way you can have the data outside the query.
On the other hand, you could also use $.ajax to do the query. It's just another way of doing things, a little more "tweakable". This example have some error handlers that will help you identify the problem.
Something like this:
var JSONdata = $.ajax({
type : "GET",
url : "MapDatabroke.json",
cache : false,
dataType: "json", // Use when retrieving
success: function(data) {
// do something here after retrieving the "data".
},
error: function (xhr, ajaxOptions, thrownError) {
//Add these parameters to display the required response
console.log( 'xhr.status: ' + xhr.status );
console.log( 'thrownError: ' + thrownError );
}
});
Also, check your json data (http://jsonlint.com/). Sometimes there is a comma where should not, or a missing bracket.
Related
Is there a way to use callback functions in Python with requests? This is from a here API example im trying to copy using python. This is the request and the function used at the end in the parameters is defined jsoncallback=parseResponse.
If i can replace the functionality of the callback in a other way would be fine too, is it possible to just take the response and do everything thats done in parseRespone?
"https://route.api.here.com/routing/7.2/calculateroute.json?jsonAttributes=1&waypoint0=50.7799,6.08425&waypoint1=50.77988,6.08288&waypoint2=50.78144,6.07794&representation=overview&routeattributes=sc,sm,sh,bb,lg,no,shape&legattributes=li&linkattributes=sh,nl,fc&mode=fastest;car;traffic:enabled&app_id=inCUge3uprAQEtRaruyaZ8&app_code=9Vyk_MElhgPCytA7z3iuPA&jsoncallback=parseResponse"
var parseResponse = function (resp)
{
if (resp.error != undefined)
{
alert (resp.error);
feedbackTxt.innerHTML = resp.error;
return;
}
if (resp.response == undefined)
{
alert (resp.subtype + " " + resp.details);
feedbackTxt.innerHTML = resp.error;
return;
}
//add Routing Release number if not already done
if (releaseRoutingShown == false){
releaseInfoTxt.innerHTML+="<br />HLP Routing: "+resp.response.metaInfo.moduleVersion;
routerMapRelease = resp.response.metaInfo.mapVersion;
mapReleaseTxt.innerHTML = "HLP Routing Service based on "+routerMapRelease+ " map release";
releaseRoutingShown = true;
}
var strip = new H.geo.Strip(),
shape = resp.response.route[0].shape,
i,
l = shape.length;
for(i = 0; i < l; i++)
{
strip.pushLatLngAlt.apply(strip, shape[i].split(',').map(function(item) { return parseFloat(item); }));
}
polyline = new H.map.Polyline(strip,
{
style:
{
lineWidth: 5,
strokeColor: "rgba(18, 65, 145, 0.7)",
lineJoin: "round"
}
});
group.addObject(polyline);
var links = [];
for(var i = 0; i < resp.response.route[0].leg.length; i++)
links = links.concat(resp.response.route[0].leg[i].link);
pdeManager.setLinks(links);
pdeManager.setBoundingBoxContainer(group);
pdeManager.setOnTileLoadingFinished(pdeManagerFinished);
pdeManager.start();
}
function pdeManagerFinished(finishedRequests)
{
feedbackTxt.innerHTML = "Done. Requested " + finishedRequests + " PDE tiles for " + numLinksMatched + " route links. ";
var resultHTML = '<table class="pde_table" cellpadding="2" cellspacing="0" border="1" width="90%">' +
'<thead>' +
'<tr>' +
'<th width="80%">Sign</th>' +
'<th width="20%">#</th>' +
'</tr>' +
'</thead>' +
'<tbody id="maps_table_body">';
for(var sign in signs)
{
resultHTML += "<tr>" + "<td>" + sign + "</td>" + "<td>" + signs[sign] + "</td>" + "</tr>";
}
resultHTML += "</tbody>" + "</table>";
document.getElementById("resultArea").innerHTML = resultHTML;
document.getElementById("resultArea").style.display = "block";
map.addObject(group);
map.setViewBounds(group.getBounds());
}
You don't need to use any callback (since there's nothing that would execute it anyway); elide the parameter and call response.json().
The below code does raise
Unauthorized. The request is not from an authorized source.
so you may need some additional headers or such (possibly an Origin header if the credentials are matched to a site address).
import requests
resp = requests.get(
url="https://route.api.here.com/routing/7.2/calculateroute.json",
params={
"jsonAttributes": "1",
"waypoint0": "50.7799,6.08425",
"waypoint1": "50.77988,6.08288",
"waypoint2": "50.78144,6.07794",
"representation": "overview",
"routeattributes": "sc,sm,sh,bb,lg,no,shape",
"legattributes": "li",
"linkattributes": "sh,nl,fc",
"mode": "fastest;car;traffic:enabled",
"app_id": "inCUge3uprAQEtRaruyaZ8",
"app_code": "9Vyk_MElhgPCytA7z3iuPA",
},
)
# Uncomment this to see the actual error.
# print(resp.content)
resp.raise_for_status()
print(resp.json())
I am unsure why my getCereal variable id using the find("td:first").html() is not working. I have been able to create my table, however my click event will not work. I am stumped. Any input will be greatly appreciated.
<div id="cer"></div>
<script type="text/javascript">
// jQuery onClick event
// the click function MUST BE USED CANNOT BE ALTERED OR REMOVED
$(function () {
$("table tr").click(function (event) {
function getCereal(id) {
for (var cerId = 0; cerId < cereals.length; cerId++){
if(cereals[cerId].id == id){
alert(cereals[cerId].id +" " + cereals[cerId].name + " " +
cereals[cerId].like);
break;
}
}
}
var id = $(this).find("td:first").html();
getCereal(id)
// This creates an cereal constructor object
function cereal(id, name, like) {
this.id = id;
this.name = name;
this.like = like;
}
// This creates 5 new objects with cereal information.
const cereals = [
new cereal(1, 'Captain Crunch', 'Yes'),
new cereal(2, 'Frosted Wheats ', 'Yes'),
new cereal(3, 'Shredded Wheat', 'No'),
new cereal(4, 'Trix', 'No'),
new cereal(5, 'Count Chocula', 'No'),
];
var output = "<h1>Cereal Listing</h1><table><thead>"+"<tr>"+"<th>"+"Id"+"</th>"+"<th>"+"Cereal Name"+"</th>"+"<th>"+"Like?"+"</th>"+"</tr>"+"</thead>"
for (var x = 0; x < cereals.length; x++) {
output +='<tr>' + "<td>" + cereals[x].id + "</td>" +"<td>" + cereals[x].name + "</td>" + "<td>" + cereals[x].like +"</td>" + '</a>' + "</tr>";
}
output += "</table>";
document.getElementById('cer').innerHTML = output;
})
});
</script>
It's really unclear what the goal is here. Maybe this will help, consider the following code.
$(function() {
function cereal(id, name, like) {
this.id = id;
this.name = name;
this.like = like;
}
const cereals = [
new cereal(1, 'Captain Crunch', 'Yes'),
new cereal(2, 'Frosted Wheats ', 'Yes'),
new cereal(3, 'Shredded Wheat', 'No'),
new cereal(4, 'Trix', 'No'),
new cereal(5, 'Count Chocula', 'No'),
];
var output = "<h1>Cereal Listing</h1>";
output += "<table class='cereal-table'><thead>";
output += "<tr><th>Id</th><th>Cereal Name</th><th>Like?</th></tr>";
output += "</thead><tbody>";
$.each(cereals, function(k, c) {
var row = $("<tr>", {
"data-c-id": k
});
$("<td>").html(c.id).appendTo(row);
$("<td>").html(c.name).appendTo(row);
$("<td>").html(c.like).appendTo(row);
output += row.prop("outerHTML");
});
output += "</tbody></table>";
$("#cer").html(output);
$(".cereal-table").on("click", "tr", function(e) {
var cId = parseInt($(this).data("c-id"));
console.log("Row C-ID: " + cId);
var data = "";
data += "ID: " + cereals[cId].id;
data += ", Name: " + cereals[cId].name;
data += ", Like: " + cereals[cId].like
alert(data);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="cer"></div>
Since jQuery is a JavaScript Framework, you can mix and match both, yet I try to remain in one or the other. This is all in jQuery.
The function creates an Object. You have 5 objects in an Array and we're going to iterate the Array using $.each(). This is a functioned designed for this. See:
jQuery.each()
Each Object has parameters we can call and insert into the Table Cell elements <td>. jQuery give us the ability to quickly create elements as jQuery Objects: $("<td>").
Since the goal appears to be to create an output string of HTML text, we can convert all the jQuery Objects we've create into HTML by asking for the outerHTML property.
The result of running the code is:
<h1>Cereal Listing</h1><table><thead><tr><th>Id</th><th>Cereal Name</th><th>Like?</th></tr></thead><tbody><tr><td>1</td><td>Captain Crunch</td><td>Yes</td></tr><tr><td>2</td><td>Frosted Wheats </td><td>Yes</td></tr><tr><td>3</td><td>Shredded Wheat</td><td>No</td></tr><tr><td>4</td><td>Trix</td><td>No</td></tr><tr><td>5</td><td>Count Chocula</td><td>No</td></tr></tbody></table>
Once the table is constructed and outputted, you can bind a click event to the Row with .on() or .click(). I advise the .on() since it more tolerate of dynamic content.
We bind the click event to each row and then collect the data from the row and create an alert.
Hope this helps.
I'm working through the Google Place API documentation and I'm trying to get a script that pulls PlaceIDs from a webpage, and replace them with output from the Google Place API.
I managed to successfully get an output from multiple Place IDs by duplicating the code and changing the variable and function names, but now I'm trying to create a loop function so that I'm not duplicating code. Below is what I have, but I'm getting an error. By looking at the console, it seems to work up till the Callback function where it beaks down.
"Uncaught TypeError: Cannot set property 'innerHTML' of null
at callback (places.html:29)"
I've tried a few things, but no luck so far. Any suggestions would be appreciated. Thanks,
<body>
<div id="MY0">ChIJaZ6Hg4iAhYARxTsHnDFJ9zE</div>
<div id="MY1">ChIJT9e323V644kRR6TiEnwcOlA</div>
<script>
var request = [];
var service = [];
var div = [];
for (i = 0; i < 2; i++) {
request[i] = {
placeId: document.getElementById("MY" + i).innerHTML,
fields: ['name', 'rating', 'formatted_phone_number', 'geometry', 'reviews', 'photos'],
};
service[i] = new google.maps.places.PlacesService(document.createElement('div'));
service[i].getDetails(request[i], callback);
function callback(place, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
div[i] = document.getElementById("MY" + i);
div[i].innerHTML = "<b>" + place.name + "</b><br>" + place.rating + "<br>" + place.reviews[1].author_name + "<br>" + place.reviews[1].rating + "<br>" + place.reviews[1].text + "<br><img src='" + place.photos[0].getUrl({'maxWidth': 250, 'maxHeight': 250}) + "'>";
}
}
}
</script>
</body>
Move the callback outside of the for loop and forget about the array named div (unless you need this...if so I will rewrite). The for loop is executing before the getDetails() call returns any result, because this call is asynchronous - since you don't have much control over the Google Places callback, I would save the IDs in an array and then use them in callback, like this:
function gp_callback(place, status) {
var el = document.getElementById(window.id_set[0]); // first in first out - the for loop should populate the IDs in correct order
if (status == google.maps.places.PlacesServiceStatus.OK) {
el.innerHTML = "<b>" + place.name + "</b><br>" + place.rating + "<br>" + place.reviews[1].author_name + "<br>" + place.reviews[1].rating + "<br>" + place.reviews[1].text + "<br><img src='" + place.photos[0].getUrl({'maxWidth': 250, 'maxHeight': 250}) + "'>";
}
if (window.id_set.length > 1) {
window.id_set.splice(0, 1); // remove first element from array because has been used - now the next element is at index 0 for the next async callback
}
}
var request = [];
var service = [];
var id_set = [];
for (i = 0; i < 2; i++) {
request[i] = {
placeId: document.getElementById("MY" + i).innerHTML,
fields: ['name', 'rating', 'formatted_phone_number', 'geometry', 'reviews', 'photos'],
};
id_set.push("MY" + i); // this ensures array is populated (in proper order, b/c it tracks the execution of the for loop) for use in callback before callback is called (since getDetails() is async)
service[i] = new google.maps.places.PlacesService(document.createElement('div'));
service[i].getDetails(request[i], function(place, status) {
gp_callback(place, status);
});
}
UPDATE: More scalable and elegant answer after I had a little more time to think about it.
<div id="MY0" class="gp_container">ChIJaZ6Hg4iAhYARxTsHnDFJ9zE</div>
<div id="MY1" class="gp_container">ChIJT9e323V644kRR6TiEnwcOlA</div>
.
.
.
<div id="MYN" class="gp_container">fvbfsvkjfbvkfvb</div> // the nth div
<script>
function populate_container(place, status, container_id) {
var el = document.getElementById(container_id);
if (status == google.maps.places.PlacesServiceStatus.OK) {
el.innerHTML = "<b>" + place.name + "</b><br>" + place.rating + "<br>" + place.reviews[1].author_name + "<br>" + place.reviews[1].rating + "<br>" + place.reviews[1].text + "<br><img src='" + place.photos[0].getUrl({'maxWidth': 250, 'maxHeight': 250}) + "'>";
}
}
function call_service(id_request_map) {
var i, container_id, request,
service_call = function(container_id, request) {
var service = new google.maps.places.PlacesService(document.createElement('div'));
service.getDetails(request, function(place, status) {
populate_container(place, status, container_id);
});
};
for(i in id_request_map) {
service_call(i, id_request_map[i]);
}
}
$(document).ready(function() {
var request, container_id,
id_request_map = {},
container_length = document.getElementsByClassName("gp_container").length,
i = 0;
for (; i < container_length; i++) {
container_id = "MY" + i;
request = {
placeId: document.getElementById(container_id).innerHTML,
fields: ['name', 'rating', 'formatted_phone_number', 'geometry', 'reviews', 'photos'],
};
id_request_map[container_id] = request; // build the association map
}
call_service(id_request_map);
});
</script>
I created a function for reusing the splice feature for javascript arrays however, after I run it once, it cannot be reused.
var removePerson = function(d, person_id) {
var person_index = d.findIndex(i => i.id == person_id);
d.splice(person_index, 1);
return d;
};
I am not getting console errors. I do not know how to debug it. Here is my JSFiddle.
If you run the example, you will see you can remove any 1 person from the list, but when you try to remove either of the remaining 2, nothing happens (e.g. console errors, console response). Any idea how I can support reuse for my removePerson() function?
Your solution doesn't work because of how your populateList works.
In your populateList, you have a line:
$('#load').empty();
This line empties the table and removes the buttons attached with click event listener.
Then, you add completely new button.delete, which aren't attached with any event listener.
To solve this, you can put your .on() into populateList function.
var populateList = function(d) {
$("#load").empty();
var new_rows;
for(var i = 0; i < d.length; i++) {
new_rows += "<tr>" +
"<td>" + d[i].id + "</td>" +
"<td>" + d[i].name + "</td>" +
"<td>" + d[i].phone + "</td>" +
"<td><button class='delete' data-id='" + d[i].id + "'>delete</button></td>" +
"</tr>";
}
$("#load").append(new_rows);
// delete event
$(".delete").on("click", function() {
var delete_sel = $(this).attr("data-id");
populateList(removePerson(d, delete_sel));
});
};
Here's a working jsFiddle.
Alternatively, you can use solution from this answer (which is a cleaner solution imo).
$("table").on("click",".delete", function() {
var delete_sel = $(this).attr("data-id");
populateList(removePerson(data, delete_sel));
});
More explanation on why his answer works on jQuery documentation (Look at the selector parameter).
Try this code:JSFiddle
or the code snippet:
var removePerson = function(d, person_id) {
var person_index = d.findIndex(i => i.id == person_id);
d.splice(person_index, 1);
return d;
};
var populateList = function(d) {
$("#load").empty();
var new_rows;
for(var i = 0; i < d.length; i++) {
new_rows += "<tr>" +
"<td>" + d[i].id + "</td>" +
"<td>" + d[i].name + "</td>" +
"<td>" + d[i].phone + "</td>" +
"<td><button class='delete' data-id='" + d[i].id + "'>delete</button></td>" +
"</tr>";
}
$("#load").append(new_rows);
};
$(document).ready( function() {
// initial list
var data = [
{
"id": 1001,
"name": "Andy Roy",
"phone": "555-555-5551"
},
{
"id": 1002,
"name": "Bob Dillon",
"phone": "555-555-5552"
},
{
"id": 1003,
"name": "Carl Sagan",
"phone": "555-555-5553"
}
];
//initial populate list
populateList(data);
// delete event
$("table").on("click",".delete", function() {
var delete_sel = $(this).attr("data-id");
populateList(removePerson(data, delete_sel));
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table border>
<thead>
<tr><th>ID</th><th>Name</th><th>Phone</th></tr>
</thead>
<tbody id="load"></tbody>
</table>
$("table").on("click",".delete", function() {
var delete_sel = $(this).attr("data-id");
populateList(removePerson(data, delete_sel));
});
I am seeking help trying to add a new table in my third function called ingredients. I am not very familiar with javascript so I tried to duplicate code from newDosage which is similar to what I need to do. Unfortunately, right now all I see is 0, 1, or 2 and not the actual text from the ingredient table. If anyone can help me correctly call the table, it would be greatly appreciated. Thank you.
Below is my code. The first function pulls the database, the second function uses the results and the third function is where I have tried to add the ingredient table.
function listTreatmentDb(tx) {
var category = getUrlVars().category;
var mainsymptom = getUrlVars().mainsymptom;
var addsymptom = getUrlVars().addsymptom;
tx.executeSql('SELECT * FROM `Main Database` WHERE Category="' + category +
'" AND Main_Symptom="' + mainsymptom + '" AND Add_Symptom="' + addsymptom + '"',[],txSuccessListTreatment);
}
function txSuccessListTreatment(tx,results) {
var tubeDest = "#products";
var len = results.rows.length;
var treat;
for (var i=0; i < len; i = i + 1) {
treat = results.rows.item(i);
$("#warning").append("<li class='treatment'>" + treat.Tips + "</li>");
$("#warning-text").text(treat.Tips);
$('#warning').listview('refresh');
//console.log("Specialty Product #1: " + treat.Specialty1);
if(treat.Specialty1){
$("#products").append(formatProductDisplay('specialty1', treat.Specialty1, treat.PurposeSpecialty1, treat.DosageSpecialty1, '1'));
}
if(treat.Specialty2){
$("#products").append(formatProductDisplay('specialty2', treat.Specialty2, treat.PurposeSpecialty2, treat.DosageSpecialty2, '0'));
}
}
}
function formatProductDisplay(type, productName, productPurpose, productDosage, Ingredients, aster){
var newDosage = productDosage.replace(/"\n"/g, "");
if(aster=='1'){ productHTML += "*" }
productHTML+= "</div>" +
"</div>" +
"<div class='productdose'><div class='label'>dosage:</div>" + newDosage + "</div>" +
"<div class='productdose'><div class='label'>ingredients:</div>" + Ingredients +
"</div></li>"
return productHTML;
}
You are missing an argument when you call formatProductDisplay(). You forgot to pass in treat.Ingredient.
Change:
$("#products").append(formatProductDisplay('specialty1', treat.Specialty1, treat.PurposeSpecialty1, treat.DosageSpecialty1, '1'));
To:
$("#products").append(formatProductDisplay('specialty1', treat.Specialty1, treat.PurposeSpecialty1, treat.DosageSpecialty1, treat.Ingredients, '1'));
Also do the same thing to the similar 'Specialty2' line right below it.