I posted a question regarding this a few days ago and the advice I got seemed to work, partially, with returning the results I wanted, but with the final rendering of data, I ran into my last issue. Here's the original post if you'd like to refer to it: Grab the value of a span element
I rewrote all of my code using Fetch instead of the older xmlHttp API since that was recommended in the previous post. I also wanted to break the functions up a bit into other functions for readability.
You'll see that the fetch API is making a call to another database (I had to hide the headers for privacy reasons) and calling the callFetch
function. The callFetch
function essentially takes the data
object and runs the addresses from data
through Google's distanceMatrix API in order to compare the user's input address (repeated the same number of times as the addresses
array) to the addresses in the data
object. Inside of getDistance
, I'm getting all of the compared results and pushing the results to separated arrays.
Inside of getSortedValues
, I'm taking my results from getDistance
, sorting them by mileage (which is one of the more important parts to what I want to return in my rendered data) then passing them as separate objects (facility
, distance
, time
, address
) to an array called combined
.
Lastly, renderTemplate
takes combined and renders the html with values from each combined object's values and renders to the page dynamically.
I updated some of my code and am attempting to clarify a bit more. Buttons requested that I add mock values since there's no way for users to test the code. I tried that, but it changed the behavior of my code and took the dynamic part out of pulling data from another source where the problem of awaiting no longer existed.
I believe my main problem lies in sortValues
. You'll see the first time everything fires (in the image) when I hit the submit button (I added console logs to each function to better understand the sequence of functions firing) sortValues returns an empty array. The second time the array populates with all four objects of 15 values each. So, I believe, regardless of me adding async
to renderTemplate
and await
ing sortValues
, sortValues
is still going to return an empty array the first time around. I think the root of all this is waiting for combined
to populate inside of sortValues
. Does anyone know what I can do to wait or get combined
to populate the first time sortValues
is called?
let inputVal = [];
let facilityArray = [];
let distanceArray = [];
let timeArray = [];
let filteredArray = [];
let sortedArray = [];
let combined = [];
let facAddress = [];
let resultsObject, intDistanceResult;
const resetButton = document.querySelector("#refresh");
// Set data object
const data = {
from: "xxxxxxxxx",
// Facility id and address of each location
select: [6, 8],
options: {
skip: 0,
top: 0,
},
};
fetchAPI = () => {
fetch("https://api.quickbase.com/v1/records/query", {
method: "POST",
headers: {
"QB-Realm-Hostname": "xxxxx.quickbase.com",
"Authorization": "QB-USER-TOKEN xxxxxxxxxxxxxxxxxx",
"Content-Type": "application/json",
},
body: JSON.stringify(data),
})
.then(resp => resp.json())
.then(data => callFetch(data))
.catch(error => console.log("Error:", error));
console.log("fetchAPI fired")
};
callFetch = dataObject => {
console.log("callFetch fired");
// Map through the data object and grab the facId and address of each location
const facId = dataObject.data.map(e => e["6"].value);
const addresses = dataObject.data.map(e => e["8"].value);
// Create Google Maps distance service instance
distanceService = new google.maps.DistanceMatrixService();
for (let i = 0; i < addresses.length; i++) {
// Add matrix settings object
distanceService.getDistanceMatrix({
origins: [inputVal.toString("")],
destinations: [addresses[i]],
travelMode: "DRIVING",
unitSystem: google.maps.UnitSystem.IMPERIAL,
durationInTraffic: true,
avoidHighways: false,
avoidTolls: false,
},
// Set response and error capture
(response, status) => {
if (status !== google.maps.DistanceMatrixStatus.OK) {
console.log("Error:", status);
const message = document.querySelector("#message");
message.innerHTML = `Error: ${status}. Please resubmit.`;
} else {
const distanceResult = response.rows[0].elements[0].distance
.text;
const timeResult = response.rows[0].elements[0].duration.text;
// Convert distanceResult values to integers and push to distanceArray to later sort
intDistanceResult = parseInt(distanceResult.replace(/,/g, ""));
// Push results to respective arrays to pass to combined object inside of sortValues function
facilityArray.push(facId[i]);
distanceArray.push(intDistanceResult);
timeArray.push(timeResult);
facAddress.push(addresses);
}
}
);
}
renderTemplate();
};
sortValues = () => {
console.log("sortValues fired");
// Re-sort array of objects from getDistance function with values in order by mileage, ascending
combined = facilityArray
.map((facility, i) => ({
facility,
distance: distanceArray[i],
time: timeArray[i],
address: facAddress[i]
}))
.sort((first, second) => first.distance - second.distance);
console.log(combined);
};
renderTemplate = async() => {
await sortValues();
console.log("renderTemplate fired");
// Grab container div entry point in html
let container = document.querySelector(".container-sm");
for (let i = 0; i < combined.length; i++) {
// Create html dynamically and populate based on combined' objects values
let div = document.createElement("div");
div.classList.add("d-inline-flex", "p-2", "mb-1");
div.innerHTML =
`<div class="container">
<div class="card" style="width: 20rem; height: fixed;">
<div class="card-body">
<input class="form-control" hidden value="${inputVal.join('')}" type="text" placeholder="Destination Address" id="destaddress${i}">
<input class="form-control" hidden readonly type="text" placeholder="Start Address" id="startaddress${i}">
<h6 class="card-title" style="font-weight: bold">Service Center - ${combined[i].facility}</h6>
<h6 class="card-title">Distance - <span id="distance${i}">${combined[i].distance} miles</span></h6>
<h6 class="card-title">Drive Time - <span id="time${i}">${combined[i].time}</span></h6>
</div>
</div>
</div>`;
// Append new values to the div
container.appendChild(div);
}
}
form.addEventListener("submit", (e) => {
e.preventDefault();
const patientAddressInput = document.querySelector("#patientaddress");
// Get user entry and assign to inputVal in dynamically created HTML
inputVal.push(patientAddressInput.value);
fetchAPI();
console.log("Listener fired");
});
resetButton.addEventListener("click", () => location.reload());
<!DOCTYPE html>
<html>
<head>
<title>Ethos Service Center - Google Maps Distance Search</title>
<link rel="stylesheet" type="text/css" href="style.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
</head>
<body>
<div class="container-sm mt-3">
<form class="form mb-3" id="form">
<div class="search">
<div class="card-body">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">Patient Destination</span>
</div>
<input class="form-control" type="text" placeholder="Enter Zip Code" class="patientaddress" id="patientaddress" required>
<div class="input-group-prepend">
<span class="input-group-text" id="message"></span>
</div>
</div>
<hr>
<button class="btn btn-primary mt-2" type="submit" id="submit">Submit</button>
<button class="btn btn-outline-success mt-2 ml-3" type="reset" value="Reset" id="refresh">Clear Destination</button>
</div>
</div>
</form>
</div>
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&libraries=places&key=[Google Maps API Key]"></script>
<script type="text/javascript" src="main.js"></script>
</body>
</html>