Declaring elements without them being inside a function - javascript

Hi guys am having trouble with this, When I declare the elements outside the loop the script ends up being broken. I plan to use the elements in several functions. Tried a lot of times but didn't find a solution. Broke the project a few times while doing guess work. Also If there's any idea on how to shorten this or to make it work better please give me a heads up :)
async function loadTable() {
try {
while(dpTable.firstChild) dpTable.removeChild(dpTable.firstChild);
const result = await fetch('http://localhost:5000/users/student_data', { method: 'GET' });
const table = await result.json();
table.forEach((res) => {
//Create Table elements and then load results into the elements
const tr = document.createElement('tr');
const studentNo = document.createElement('td');
const name = document.createElement('td');
const surname = document.createElement('td');
const date_joined = document.createElement('td');
const fees_paid = document.createElement('td');
const fees_owing = document.createElement('td');
const is_owing = document.createElement('td');
const trash_btn = document.createElement('button');
const trash_Icon = document.createElement('i');
tr.id = 'Content';
studentNo.id = res.id;
name.id = 'fname';
surname.id = 'lname';
fees_paid.id = 'amount_paid';
fees_owing.id = 'amount_owing';
is_owing.id = 'debt';
//Enter the data from the response
studentNo.innerText = res.id;
name.innerText = res.fname;
surname.innerText = res.lname;
fees_paid.innerText = res.amount_paid;
fees_owing.innerText = res.amount_owing;
date_joined.innerText = res.date_joined;
is_owing.innerText = res.is_owing;
trash_btn.innerText = 'Delete';
//Add Classes for elements
studentNo.classList.add('trContent');
name.classList.add('trContent');
surname.classList.add('trContent');
fees_paid.classList.add('trContent');
fees_owing.classList.add('trContent');
date_joined.classList.add('trContent');
is_owing.classList.add('trContent');
trash_Icon.classList.add('fas');
trash_Icon.classList.add('fa-trash');
trash_Icon.classList.add('trash-btn');
//Append table row elements to main table
tr.append(studentNo);
tr.append(name);
tr.append(surname);
tr.append(fees_paid);
tr.append(fees_owing);
tr.append(date_joined);
tr.append(is_owing);
tr.append(trash_btn);
//Event Listeners
trash_btn.addEventListener('click', async (e) => {
if (window.confirm('Delete')) {
console.log('trash icon clicked');
const jsonReq = {};
jsonReq.id = res.id;
const result = await fetch('http://localhost:5000/users/student_data', {
method: 'DELETE',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(jsonReq),
});
alert('Deleted Record');
window.location.reload();
}
});
//Append the table as a child to the main element
dpTable.appendChild(tr);
});
} catch (e) {
console.log(`Cant load List ${e}`);
}
}

There are a couple of minor issues you need to remedy or perhaps even ignore
Element IDs must be unique so you can't use the same id for multiple cells. You could switch to using classNames instead
The buttons need to be inserted into cells, they can't be directly insterted into a row
You can cut this all down quite a bit by using an array of field names to iterate and using TableElement.insertRow() and TableRow.insertCell()
Something like:
const fields = ['studentNo','fname','lname','amount_paid','amount_owing','date_joined','is_owing'] ;
table.forEach((res) => {
const row = dpTable.insertRow();
// loop over fields array to create cell for each
fields.forEach((f) => {
const cell = row.insertCell();
cell.innerText = res[f];
cell.className = 'trContent';
});
const btnCell = row.insertCell();// cell for button
const trash_btn = document.createElement('button');
const trash_Icon = document.createElement('i');
trash_Icon.classList.add('fas', 'fa-trash', 'trash-btn');
trash_btn.append(trash_icon);
btnCell.append(trash_btn);// insert button in own cell
trash_btn.addEventListener('click', .....);
});

It is likely because you're declaring them as const which means they shouldn't be rewritten - but you have them in a loop which reassigns them and so causes an error. You can use let instead in this case
async function loadTable() {
try {
while(dpTable.firstChild) dpTable.removeChild(dpTable.firstChild);
const result = await fetch('http://localhost:5000/users/student_data', { method: 'GET' });
const table = await result.json();
let tr, studentNo, name, surname, date_joined, fees_paid, fees_owing, is_owing, trash_btn, trash_Icon
table.forEach((res) => {
//Create Table elements and then load results into the elements
tr = document.createElement('tr');
studentNo = document.createElement('td');
name = document.createElement('td');
surname = document.createElement('td');
date_joined = document.createElement('td');
fees_paid = document.createElement('td');
fees_owing = document.createElement('td');
is_owing = document.createElement('td');
trash_btn = document.createElement('button');
trash_Icon = document.createElement('i');
tr.id = 'Content';
studentNo.id = res.id;
name.id = 'fname';
surname.id = 'lname';
fees_paid.id = 'amount_paid';
fees_owing.id = 'amount_owing';
is_owing.id = 'debt';
//Enter the data from the response
studentNo.innerText = res.id;
name.innerText = res.fname;
surname.innerText = res.lname;
fees_paid.innerText = res.amount_paid;
fees_owing.innerText = res.amount_owing;
date_joined.innerText = res.date_joined;
is_owing.innerText = res.is_owing;
trash_btn.innerText = 'Delete';
//Add Classes for elements
studentNo.classList.add('trContent');
name.classList.add('trContent');
surname.classList.add('trContent');
fees_paid.classList.add('trContent');
fees_owing.classList.add('trContent');
date_joined.classList.add('trContent');
is_owing.classList.add('trContent');
trash_Icon.classList.add('fas');
trash_Icon.classList.add('fa-trash');
trash_Icon.classList.add('trash-btn');
//Append table row elements to main table
tr.append(studentNo);
tr.append(name);
tr.append(surname);
tr.append(fees_paid);
tr.append(fees_owing);
tr.append(date_joined);
tr.append(is_owing);
tr.append(trash_btn);
//Event Listeners
trash_btn.addEventListener('click', async (e) => {
if (window.confirm('Delete')) {
console.log('trash icon clicked');
const jsonReq = {};
jsonReq.id = res.id;
const result = await fetch('http://localhost:5000/users/student_data', {
method: 'DELETE',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(jsonReq),
});
alert('Deleted Record');
window.location.reload();
}
});
//Append the table as a child to the main element
dpTable.appendChild(tr);
});
} catch (e) {
console.log(`Cant load List ${e}`);
}
}

You can definitely simplify your code and make it more flexible into the process. The JSON data returned will have a set of keys - these can be used to build the HTML output without knowing, in advance, what they are. The beauty of that is the same code can display greatly different datasets very easily.
If you were to use the method create shown below to create the new DOM elements you greatly reduce the code needed.
<?php
#---------------------------------------------------
# to emulate whatever "users/student_data" returns.
# this is dummy data for example only.
#
set_include_path('c:/wwwroot/dbo/');
require 'db-conn-details.php';
require 'mysqli-conn.php';
/*
When we receive the AJAX request, identified by querystring,
prepare some sql statement and execute. Return the full
recordset in JSON format.
*/
if( isset( $_GET['fetch'] ) && $_GET['fetch']=='data' ){
ob_clean();
$sql='select * from `sport` limit 10';
$res=$db->query( $sql );
$json=json_encode( $res->fetch_all( MYSQLI_ASSOC ) );
exit( $json );
}
if( $_SERVER['REQUEST_METHOD']=='DELETE' ){
exit('DELETE');
}
?>
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8' />
<title></title>
<style>
#data{ border:1px solid grey;display:table;padding:1rem;width:100%; }
</style>
<script>
// The endpoint used by the ajax query - in this example it is same page.
const url=location.href; //http://localhost:5000/users/student_data
const create=function(t,a,p){
/*
utility to add a new node with attributes and content.
t=type ~ the DOM node type, defaults to DIV
a=attrbutes to set
p=parent to append new node to.
*/
let el=(typeof(t)=='undefined'||t==null)?document.createElement('div'):document.createElement(t);
let _arr=['innerHTML','innerText','html','text'];
for(let x in a)if(a.hasOwnProperty(x)&&!~_arr.indexOf(x))el.setAttribute(x,a[x]);
if(a.hasOwnProperty('innerHTML')||a.hasOwnProperty('html'))el.innerHTML=a.innerHTML||a.html;
if(a.hasOwnProperty('innerText')||a.hasOwnProperty('text'))el.innerText=a.innerText||a.text;
if(p!=null )typeof(p)=='object'?p.appendChild(el):document.getElementById(p).appendChild(el);
return el;
};
async function loadTable() {
try {
// It was not clear where dbTable was defined - hence declared here
const dpTable=document.querySelector('table#data');
while( dpTable.firstChild ) dpTable.removeChild( dpTable.firstChild );
// As this example targets the same page I needed a way to return only db results - hence using querystring.
const result = await fetch( url + '?fetch=data' );
const json = await result.json();
// the callback that will process the button clicks. The button has a dataset attribute set
// that will use the res.id value from each JSON record.
const clickhandler=async function(e){
if( window.confirm('Delete') ) {
const jsonReq = {
id:this.dataset.id
};
const result = await fetch( url, {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify( jsonReq ),
});
console.log( result );
alert('Record deleted');
window.location.reload();
}
};
json.forEach( res => {
// find all the keys from the JSON ~ one of which will be the ID for the record ( res.id )
let fields=Object.keys( res );
// generate new table row
let tr=dpTable.insertRow();
// iterate through all json keys - add new table cell with JSON data.
fields.forEach( field => {
create('td',{
'data-id':field,
text:res[field]
},tr);
});
// add the button for the row and assign listener - also assign the dataset attribute for the record ID.
let bttn=create('button',{ text:'Delete','data-id':res.id }, tr.insertCell() );
bttn.addEventListener( 'click', clickhandler );
// add the icon to the button
create('i',{ 'class':'fas fa-trash trash-btn' }, bttn );
});
} catch(e) {
console.log('Unable to load list: %o',e);
}
}
document.addEventListener('DOMContentLoaded',loadTable);
</script>
</head>
<body>
<table id='data'></table>
</body>
</html>
The sql query shown here in the demo returns the following data:
[
{"id":"1","tag":"3D Archery","description":"a form of archery in which the goal is to strike targets in the shape of animals at unknown distances.","enabled":"1"},
{"id":"2","tag":"3x3","description":"a variation of basketball played on a half court with just three players in each team.","enabled":"1"},
{"id":"3","tag":"Abseiling","description":"an adventure sport where the participants descend a steep formation using a rope. (not really a sport).","enabled":"1"},
{"id":"4","tag":"Acroski","description":"athletes on snow skis perform various choreographed\u00c2\u00a0routines (once called Ski Ballet)","enabled":"1"},
{"id":"5","tag":"Adventure Racing","description":"an event combining two or more endurance disciplines, such as Orienteering, Cross-Country Running, Mountain Biking, Paddling and Climbing. It is also called Expedition Racing.","enabled":"1"},
{"id":"6","tag":"Aerials","description":"a freestyle skiing discipline in which athletes ski along a take-off ramp, then perform various in-air tricks.","enabled":"1"},
{"id":"7","tag":"Aerobatics","description":"sport aerobatics involves aircraft maneuvers such as rolls, loops, stall turns (hammerheads), and tailslides.","enabled":"1"},
{"id":"8","tag":"Acrobatic Gymnastics","description":"team of gymnasts work together to perform acrobatic moves in combination with dance moves.","enabled":"1"},
{"id":"9","tag":"Aerobic Gymnastics","description":"another name for Sport Aerobics.","enabled":"1"},{"id":"10","tag":"Aeromodeling","description":"activity using remotely controlled flying model aircraft (not really a sport).","enabled":"1"}
]
And yielded the following output:

Related

Js: show JSON data in HTML

below i have JSON data for cafe menu and i want to view them in HTMl page as grid view showing image, name and price ... is there a way to do this in JS and HTML?
[
{
"placeImage": "assets/images/straw-1.jpg",
"placeName": " pizza",
"price": 15000
},
{
"placeImage": "assets/images/straw-1.jpg",
"placeName": " Burger",
"price": 15000
},
]
This partly depends on if you're processing that data on the server side or the client side. Either way, you can access the JSON data in your JS script like this:
const cafeData = require('./cafe.json'); // If it is in the same directory as the script
console.log(cafeData);
If you're trying to create a dynamic HTML page with data from a server, then try using a templating language like EJS, which is the simplest to learn for a JavaScript developer. If you're on the client side then you'll need to use the DOM to insert that data into your HTML. For example, if you have an element in a product card like this: <div id="pizzaPriceDisplay">
Then you're JS code might look something like this:
const priceDisplay = document.querySelector('#pizzaPriceDisplay');
pizzaPriceDisplay.innerText = '$' + cafeData[0].price;
Hope I helped and happy coding :)
Sure! Lets asume that your JSON data is stored in a javascript string
const jsonData = "..."; //The JSON string
The first thing to do would be to turn that json data into a javascript object that we can iterate. We can do that like this:
const data = JSON.parse(jsonData);
Cool, now, as far as i can tell you want to turn every item into an HTML element (like a card) and display all of them in a grid. The grid part can be pretty trivial now days using CSS Grid: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout.
So somewhere in your HTML you would want to have an element like this:
...
<div class="grid-container" id="menu-items-container">
<!-- We are going to insert your rendered data here -->
</div>
...
And somewhere in your css you will have something like this
...
.grid-container{
display:grid;
/* ... */
}
...
Now for the fun part. Taking your the contents of that data variable and turning them into html
//This function will take the data of one item
//And return an html element
function renderItem(item){
const base = document.createElement("div");
const img = document.createElement("img");
const title = document.createElement("h1");
const price = document.createElement("h2");
img.src = item.placeImage;
title.textContent = item.placeName;
price.textContent = item.price;
base.appendChild(img);
base.appendChild(title);
base.appendChild(price);
return base;
}
const container = document.querySelector("#menu-items-container");
for (let item of data){
container.appendChild(renderItem(item));
}
And that's about it, you'll probably want to style the inserted elements a little better, which you can do adding a few classes here and there. But you probably get the gist of it.
For the particular data you gave in the example this should produce something similar to this:
<div class="grid-container" id="menu-items-container">
<div>
<img src="assets/images/straw-1.jpg"/>
<h1>pizza</h1>
<h2>15000</h2>
</div>
<div>
<img src="assets/images/straw-1.jpg"/>
<h1>Burger</h1>
<h2>15000</h2>
</div>
</div>
Simply parse the JSON to JS object
let menu = JSON.parse('[{"placeImage": "assets/images/straw-1.jpg", "placeName": "Pizza", "price": 15000},{"placeImage": "assets/images/straw-1.jpg", "placeName": "Burger","price": 15000}]');
Then you can iterate over it using a loop and draw the HTML you want ex: Table
function drawMenu(menu) {
let tableElement = document.createElement("table");
// Draw the headers of the table
let headerTableRowElement = document.createElement("tr");
let imageHeaderElement = document.createElement("th");
imageHeaderElement.innerText = "Image";
headerTableRowElement.appendChild(imageHeaderElement);
let nameHeaderElement = document.createElement("th");
nameHeaderElement.innerText = "Name";
headerTableRowElement.appendChild(nameHeaderElement);
let priceHeaderElement = document.createElement("th");
priceHeaderElement.innerText = "Price";
headerTableRowElement.appendChild(priceHeaderElement);
tableElement.app.appendChild(headerTableRowElement);
// Draw the items in the menu
for (let i = 0; i < menu.length; ++i) {
let item = menu[i];
let menuItemTableRowElement = document.createElement("tr");
let imageElement = document.createElement("td");
let image = document.createElement("img");
image.setAttribute("src", item.placeImage);
imageElement.appendChild(image);
menuItemTableRowElement.appendChild(imageElement);
let nameElement = document.createElement("td");
nameElement.innerHTML = item.placeName;
menuItemTableRowElement.appendChild(nameElement);
let priceElement = document.createElement("td");
priceElement.innerHTML = item.price;
menuItemTableRowElement.appendChild(priceElement);
tableElement.appendChild(menuItemTableRowElement);
}
document.appendChild(tableElement);
}

How to call for JSON output from an API into HTML?

new to building web apps. please advise on some resources to read up if you have any good ones!
Problem:
I have created a call API on AWS - https://tnmw5vn267.execute-api.us-east-1.amazonaws.com/dev
the output is a JSON object.
However, I have no clue how to put this into a HTML page (i.e. How to get the JSON object into the HTML and then subsequently show it as a table), only:
`function CreateTableFromJSON() {
var myEmployees = [
{
"FirstName": "Benjamin",
"LastName": "Tan"
}
]
// EXTRACT VALUE FOR HTML HEADER.
// ('Book ID', 'Book Name', 'Category' and 'Price')
var col = [];
for (var i = 0; i < myEmployees.length; i++) {
for (var key in myEmployees[i]) {
if (col.indexOf(key) === -1) {
col.push(key);
}
}
}
// CREATE DYNAMIC TABLE.
var table = document.createElement("table");
// CREATE HTML TABLE HEADER ROW USING THE EXTRACTED HEADERS ABOVE.
var tr = table.insertRow(-1); // TABLE ROW.
for (var i = 0; i < col.length; i++) {
var th = document.createElement("th"); // TABLE HEADER.
th.innerHTML = col[i];
tr.appendChild(th);
}
// ADD JSON DATA TO THE TABLE AS ROWS.
for (var i = 0; i < myEmployees.length; i++) {
tr = table.insertRow(-1);
for (var j = 0; j < col.length; j++) {
var tabCell = tr.insertCell(-1);
tabCell.innerHTML = myBooks[i][col[j]];
}
}
// FINALLY ADD THE NEWLY CREATED TABLE WITH JSON DATA TO A CONTAINER.
var divContainer = document.getElementById("showData");
divContainer.innerHTML = "";
divContainer.appendChild(table);
}`
The above only generates a static table that doesn't update based on my API output. Am not sure how to do it?
Let me show you the solution.
Firstly you need to fetch JSON from API by fetch function.
After that, you need to put it to a particular HTML element by .innerHTML
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
</head>
<body>
<script>
async function fetchDataAndRenderTable() {
const serverURL = 'https://tnmw5vn267.execute-api.us-east-1.amazonaws.com/dev'
const dataFromTheServer = await fetch(serverURL).then(res => res.json())
const tableHTMLElement = document.createElement('table')
tableHTMLElement.innerHTML = `
<table style="width:100%">
<tr>
<th>FirstName</th>
<th>LastName</th>
</tr>
${
dataFromTheServer.map(item => {
return `
<tr>
<td>${item.FirstName}</td>
<td>${item.LastName}</td>
</tr>
`
})
}
</table>
`
document.body.appendChild(tableHTMLElement)
}
fetchDataAndRenderTable()
</script>
</body>
</html>
PS. But your API needs to allow CORS, otherwise you will be not able to fetch it from the browser
Here's how I might approach it using plain JS.
Use fetch to get the data from the API.
Use template literals to build up a sequence of HTML strings using map and join.
Add the final string to the page.
const json = '[{"LatestGreetingTime":"2021-06-19T15:47:10.539Z","ID":"Hello from Lambda, Benjamin Tan","FirstName":"Benjamin","LastName":"Tan"},{"LatestGreetingTime":"2021-06-19T13:44:33.761Z","ID":"Hello from Lambda, ichal shodin","FirstName":"ichal","LastName":"shodin"},{"LatestGreetingTime":"2021-06-19T13:44:33.761Z","ID":"Hello from Lambda, victoria Lovelace","FirstName":"victoria","LastName":"Lovelace"}]';
// This simulates a call to your API
function mockFetch() {
return new Promise((res, rej) => {
setTimeout(() => res(json), 1000);
});
}
// Grab the button, and a div where you can place the table
const button = document.querySelector('button');
const div = document.querySelector('div');
// Add an event listener to your button that
// callApi when it's clicked
button.addEventListener('click', callApi, false);
// As mentioned the comments you should be using
// the fetch API here, and it would look like
// fetch(url).then(res => res.json).then(buildTable)
function callApi() {
mockFetch()
.then(res => JSON.parse(res))
.then(buildTable);
}
// This function takes the data, extracts the headings
// from the first object, and then builds the row data.
// It finally puts everything together as a template literal
// and adds it to the div
function buildTable(data) {
const headings = buildHeadings(Object.keys(data[0]));
const rows = buildRows(data);
div.innerHTML = `<table><thead>${headings}</thead><tbody>${rows}</tbody></table>`;
}
// Iterate over the headings array and return heading HTML
function buildHeadings(headings) {
return headings.map(heading => `<th>${heading}</th>`).join('');
}
// Iterate over the data return row HTML
function buildRows(data) {
return data.map(row => `<tr>${buildCells(row)}</tr>`).join('');
}
// Iterate over the row data and return cell HTML
function buildCells(row) {
return Object.values(row).map(cell => `<td>${cell}</td>`).join('');
}
table { border-collapse: collapse; padding 0.2em; }
td { padding: 0.5em }
<button>Call API</button>
<div />

add items to map, collectively, one at a time, per attribute id

I have successfully been able to filter, for instance shakemaps, by attribute id - I have successfully achieved this multiple ways - but all ending with the same problem.
1.) with the where filter 2.) definition expression 3.) iterating through all attribute ids and bringing them back.
The problem: All exists for only allowing/displaying one per attribute id at a time.. my goal is to feed the attribute ids into a checkbox list (which I have done), but allowing for items via attribute id to be added to the map as they are checked, collectively, one at a time - currently I can not seem to get this to work with the aspect of having multiple or more then one appear at a time on the map.
1.) i.e. the below filter (attempted logic 1) & CodePen:
.....
function filterByID(event) {
const selectedID = event.target.getAttribute("data-id");
eqLayerView.filter = {
where: "id = '" + selectedID + "'"
};
}
view.whenLayerView(fl)
.then(function(layerView) {
eqLayerView = layerView;
eqLayerView.filter = {
where: "id = ''"
};
.............
2.) i.e. another attempted logic (adding multiple at a time here, line by line, or via array):
layer.definitionExpression = "id = 'us70008jr5'",
layer.definitionExpression = "id = 'cgi988jr52'",
3.) i.e. 3rd attempt with a suggestion here on GIS exchange: https://gis.stackexchange.com/questions/364578/loop-through-attribute-ids-of-featurelayer
layer
.load()
.then(() => {
// create a query from the layer
const query = layer.createQuery();
query.returnDistinctValues = true;
query.where = "grid_value > 2"; // or 1=1 if you need them all
// the field you want distinct values for
query.outFields = ["id"];
return layer.queryFeatures(query);
})
.then(({ features }) => {
// extract the ids to a list
const ids = features.map(({ attributes }) => attributes.id);
return ids;
})
.then((ids) => {
// You can store them how you want, in this case,
// I put them in a dropdown select menu
const fragment = document.createDocumentFragment();
ids.forEach((id) => {
const option = document.createElement('option');
option.value = id;
option.innerText = id;
fragment.appendChild(option);
});
list.appendChild(fragment);
map.add(layer);
});
All attempted logic above result in the toggling of a shakemap by attribute id to be displayed only one at a time — by toggling a new on, the previous turns off, I need the ability to have multiple being able to exist on the map at once.
ArcGIS - javascript - 4.15
I am pretty sure I answer this question a couple of weeks ago, but I can't find my answer so maybe I just thought I did, in that case sorry about that.
Anyway, with a couple of fixes your code should work. The main issue here I think is that your query is not correct if you are going to use checkboxs. You need to use a set query like IN. I would be fine if you use radio buttons.
Here you have your example with the fixes I mention,
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<title>Filter features by attribute - 4.15</title>
<link rel='stylesheet' href='https://js.arcgis.com/4.15/esri/themes/light/main.css' />
<script src='https://js.arcgis.com/4.15/'></script>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 500px;
width: 100%;
}
</style>
<script>
require([
'esri/views/MapView',
'esri/Map',
'esri/layers/FeatureLayer',
'esri/widgets/Expand'
], function (MapView, Map, FeatureLayer, Expand) {
const layer = new FeatureLayer({
url: 'https://services9.arcgis.com/RHVPKKiFTONKtxq3/arcgis/rest/services/USGS_Seismic_Data_v1/FeatureServer/1/',
outFields: ['*']
, definitionExpression: 'eventTime >= CURRENT_TIMESTAMP - 30 AND grid_value > 2'
});
const map = new Map({
basemap: 'gray-vector'
, layers: [layer]
});
const view = new MapView({
map: map,
container: 'viewDiv',
center: [-98, 40],
zoom: 4
});
let eqLayerView;
let selectedIDs = {};
function updatedFilter() {
const ids = [];
for(const [key, value] of Object.entries(selectedIDs)) {
if (value) {
ids.push(`'${key}'`);
}
}
eqLayerView.filter = {
where: `id IN (${ids.join(',')})`
};
console.log(`[updateFilter] ${eqLayerView.filter.where}`);
}
const idElement = document.getElementById('id-filter');
idElement.addEventListener('click', filterByID);
function filterByID(event) {
const chk = event.target;
console.log(`[filterByID] ${chk.getAttribute('data-id')} ${chk.checked}`);
selectedIDs[chk.getAttribute('data-id')] = chk.checked;
updatedFilter();
}
view.whenLayerView(layer).then(function (layerView) {
eqLayerView = layerView;
updatedFilter();
var query = layer.createQuery();
query.outFields = ['id'];
query.returnDistinctValues = true;
query.returnGeometry = false;
layer.queryFeatures(query).then(function (results) {
results.features.map(function (feat) {
let id = feat.attributes['id'];
let opt = document.createElement('input');
opt.type = 'checkbox';
let label = document.createElement('label')
label.innerHTML = id;
opt.className = 'id-item visible-id';
opt.setAttribute('data-id', id);
idElement.appendChild(opt);
idElement.appendChild(label);
selectedIDs[id] = false;
});
});
});
});
</script>
</head>
<body>
<div id='id-filter' class='esri-widget'>
</div>
<div id='viewDiv'></div>
</body>
</html>
BTW, I added some logs for you to see what I consider import info of the events.
Last thing, I use an auxiliary dictionary to keep track of the visible features to filter. Another way to do it would simply to query all the checkbox and add the id to the filter if it is checked.

Handling large json array on the browser to create tables

I am currently working on an inventory management web app where I load a large amount of data on an HTML table by using fetch with async.
async function getNewSummary() {
try {
let groups = await fetch(
"http://localhost:3000/dashboard/summary/groups", {
method: "GET"
}
);
let groupData = await groups.json();
console.log(json);
var table = document.getElementById("grouptable");
for (data in groupData)
table.innerHTML = `<tr>
<td>${groupData[data].groups}</td>
<td>${groupData[data].permission}</td>
</tr>`;
} catch (e) {
console.log(e);
}
}
My Express API returns a large JSON array which I loop through and create table rows from. I expected the browser to stop working when it does that. Is there any other way I can get it done?
I need to create the table and then use it as a DataTable.
Just curious, but I tried loading a pretty large dataset (28795 JSON objects) using your code as an example and it loaded pretty quickly. I adjusted the DOM manipulation to use something besides innerHTML, but other than that it is pretty much the same. Here is the request I made:
main.js
async function getNewSummary() {
try {
const groups = await fetch("https://raw.githubusercontent.com/prust/wikipedia-movie-data/master/movies.json");
const groupData = await groups.json();
console.log('Num documents: ', groupData.length);
console.log('Example document: ', groupData[0]);
const table = document.getElementById("grouptable");
for (data in groupData) {
let tr = document.createElement('tr');
let td = document.createElement('td');
td.textContent = groupData[data].title;
tr.appendChild(td);
table.appendChild(tr);
}
} catch (e) {
console.log(e);
}
}
getNewSummary();
And here is a gif of it working, it only takes about 2 seconds to create all 28,795 table rows. Could the problem be the way you are adding the data to the DOM and not the size of the dataset? Or is you dataset much larger? I've never worked with large amounts of data, so I could be wayyyy off base here, but wanted to try and help if I could.
Here is another version where I click a button that changes the background to red and adds some p tags while it's loading. It definitely hangs, but it's only for a second:
Here's my current iteration.
inside the browser
<script>
function inventoryWorker() {
w = new Worker("js/tableHelper.js");
w.onmessage = function (event) {
DatatableMaker(event.data);
}
}
function DatatableMaker(data) {
$(document).ready(function () {
$('#main').DataTable().destroy();
var events = $("#events");
// Setup text input
$("#main tfoot th").each(function () {
var title = $(this).text();
$(this).html(
'<input class="form-control" type="text" placeholder="Search ' + title +
'" />'
);
});
// DataTable
var table = $("#main").DataTable({
data: data,
paging: true,
lengthChange: true,
searching: true,
ordering: true,
info: true,
scrollX: true,
scrollCollapse: true,
autoWidth: true
});
}
</script>
In a seperate file tableHelper.js
async function getInventory() {
var tableRow = [];
try {
let inventory = await fetch("http://localhost:3000/dashboard/inventory", {
method: "GET"
});
let inventoryData = await inventory.json();
console.log(inventoryData);
for (data in inventoryData) {
var temp = [];
temp.push(
inventoryData[data].ReceivingDate,
// etc
);
tableRow.push(temp);
}
postMessage(tableRow);
} catch (e) {
console.log(e);
}
}
It works better but there is a slight pause when the inventoryWorker function is called

JS: Instantiated variable won't recognise input value

I am instantiating a new variable from a class. The class has one constructor, city and then fetches jazz clubs through the foursquare API.
When I hard-code the city name into the instantiated class, it works fine. But when I want to feed it a dynamic value (a query from the search bar which I grab through the DOM), it won't recognise the city. Here is the code:
The Class:
class Venues {
constructor(city) {
this.id = '...';
this.secret = '...';
this.city = city;
}
async getVenues() {
const response = await fetch(`https://api.foursquare.com/v2/venues/search?near=${this.city}&categoryId=4bf58dd8d48988d1e7931735&client_id=${this.id}&client_secret=${this.secret}&v=20190309`);
const venues = await response.json();
return venues;
}
}
const input = document.getElementById('search-input').value;
const button = document.getElementById('button');
const jazzClubs = new Venues(input);
button.addEventListener('click', (e) => {
e.preventDefault();
getJazzVenues();
})
function getJazzVenues() {
jazzClubs.getVenues()
.then(venues => {
console.log(venues);
})
.catch(err => {
console.log(err);
});
}
Anyone knows why the the input variable's value is not recognised by the newly instantiated jazzClubs variable?
Also, if you have tips on how to structure this code better or neater, I'd welcome any suggestions (the class definition is in a separate file already).
Many thanks guys!
Adam
You need to make sure, the following statements are triggered after the button click.
const input = document.getElementById('search-input').value;
const jazzClubs = new Venues(input);
Also your code looks too complex. Use simpler code using jquery.
Try something like this:
$(document).ready(function() {
$("#search-button").click(function() {
var searchval = $("#search-input").val();
var id = "xxx";
var secret = "yyy";
alert(searchval);
var url = "https://api.foursquare.com/v2/venues/search?near=" + searchval + "&categoryId=4bf58dd8d48988d1e7931735&client_id=" + id + "&client_secret=" + secret + "&v=20190309";
alert(url);
$.ajax({
url: url,
dataType: 'json',
success: function(data) {
var venues = data.response.venues;
alert(venues);
$.each(venues, function(i, venue) {
$('#venue-result').append(venue.name + '<br />');
});
}
});
});
});
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<label>City:</label>
<input type="text" id="search-input" />
<button type="button" id="search-button">Search</button>
<div id="venue-result"></div>
</body>
</html>

Categories