Searching from JSON - javascript

I am trying to build an AJAX search form for a dataset that queries an open data API and displays search results below the form. I want to include one or more inputs that correspond to fields within my selected JSON dataset.
When the form is submitted I want to use the form data to query the Open Data API. I want to allow users to find a subset of records from a specific dataset.
HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="style.css" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script type="text/javascript" src="data.js"></script>
<title></title>
</head>
<body>
<div class="container">
<form>
<input type="text" id="input" placeholder="what you are looking for?">
<button type="submit">Search</button>
</form>
</div>
</body>
</html>
JavaScript:
let x = document.getElementById('input');
let obj;
fetch('https://data.winnipeg.ca/resource/f58p-2ju3.json')
.then(response => {
return response.json()
})
.then(data => {
console.log(data)
obj = JSON.parse(data)
})
This is my code. I want the user to search by the location name and then the whole information will be displayed meaning observationid, observationtime, thingid and locationname in the form of a table. Only for the specific locationname entered by the user.

In case you need for searching in the loaded JSON data, you'll need 3 methods a least.
init(). This method loads the whole JSON from the API for the first time.
search(inputValue). This method takes the inputValue value in order to be used to filter the whole JSON data.
For instance, in my search() method, I'm using the toLowerCase() to turn in lowercase the current string, and indexOf() string method to find records of a location name by the first occurrence of any given character, so you could search by inserting any character of a particular location name.
search(inputValue) {
const results = this.data.filter(x => x.locationname.toLowerCase().indexOf(inputValue.toLowerCase()) > -1);
return this.renderTable(results);
}
renderTable(results). This method has the responsibility to render the filtered data given from the search(inputValue) method.
Here is a small demo where you may see it in action.
class SearchJSON {
data = null;
constructor() {
this.init();
}
init() {
fetch('https://data.winnipeg.ca/resource/f58p-2ju3.json')
.then(response => {
return response.json();
})
.then(data => {
this.data = data;
});
}
search(inputValue) {
const results = this.data.filter(x => x.locationname.toLowerCase().indexOf(inputValue.toLowerCase()) > -1);
return this.renderTable(results);
}
renderTable(results) {
let html = "<table><thead><tr><th>Observation Id</th><th>Observation Time</th><th>Thing Id</th><th>Location Name</th></tr></thead></table><table class=\"result-table\"><tbody>";
let result = null;
for (let i = 0; i < results.length; i++) {
result = results[i];
html += `<tr><td>${result.observationid}</td>
<td>${result.observationtime}</td>
<td>${result.thingid}</td>
<td>${result.locationname}</td>
<tr>`;
}
html += "</tbody></table>";
return html;
}
}
window.onload = () => {
const form = document.querySelector("form");
const input = document.getElementById("input");
const searchJSON = new SearchJSON();
form.onsubmit = (e) => {
e.preventDefault();
const result = searchJSON.search(input.value);
const formDiv = document.querySelector("form div");
const div = document.createElement("div");
div.innerHTML = result;
if (formDiv !== null) {
formDiv.parentElement.removeChild(formDiv);
form.appendChild(div);
}
form.appendChild(div);
};
};
.container {
font-size: 0.8rem;
}
.container table {
border-collapse: collapse;
}
.container table th,
.container table td {
border: #ccc solid 1px;
padding: 0.2rem 0.4rem;
width: 100px;
}
.result-table {
display: inline-block;
height: 230px;
overflow: auto;
}
<div class="container">
<form>
<input type="text" id="input" placeholder="what you are looking for?">
<button type="submit">Search</button>
</form>
</div>
I'm not sure about the capability of the endpoint but I think that It may be good to investigate if the https://data.winnipeg.ca/resource/f58p-2ju3.json endpoint can receive parameters, therefore you can send parameters to the endpoint with the given form data to return the required data so, you might no need to use the init() method from my example to load the whole JSON data at the first time.
Update: Endpoint with query string parameters:
I've found a way to use query string parameters as you needed. You may use it in this way:
`https://data.winnipeg.ca/resource/f58p-2ju3.json?locationname=${inputValue}&$limit=${limit}`
Where:
locationname is the location name to search.
$limit is the max quantity of records to retrieve by the endpoint with the current parameters.
And also, you may use async/await fetch requests like this:
async search(inputValue, limit) {
const response = await fetch(`https://data.winnipeg.ca/resource/f58p-2ju3.json?locationname=${inputValue}&$limit=${limit}`);
const results = await response.json();
return this.renderTable(results);
}
You may see it in action here:
class SearchJSON {
data = null;
async search(inputValue, limit) {
const response = await fetch(`https://data.winnipeg.ca/resource/f58p-2ju3.json?locationname=${inputValue}&$limit=${limit}`);
const results = await response.json();
return this.renderTable(results);
}
renderTable(results) {
let html = "<table><thead><tr><th>Observation Id</th><th>Observation Time</th><th>Thing Id</th><th>Location Name</th></tr></thead></table><table class=\"result-table\"><tbody>";
let result = null;
for (let i = 0; i < results.length; i++) {
result = results[i];
html += `<tr><td>${result.observationid}</td>
<td>${result.observationtime}</td>
<td>${result.thingid}</td>
<td>${result.locationname}</td>
<tr>`;
}
html += "</tbody></table>";
return html;
}
}
window.onload = () => {
const form = document.querySelector("form");
const txtLimit = document.getElementById("txtLimit");
const input = document.getElementById("input");
const searchJSON = new SearchJSON();
form.onsubmit = async(e) => {
e.preventDefault();
const result = await searchJSON.search(input.value, txtLimit.value);
const formDiv = document.querySelector("form div");
const div = document.createElement("div");
div.innerHTML = result;
if (formDiv !== null) {
formDiv.parentElement.removeChild(formDiv);
form.appendChild(div);
}
form.appendChild(div);
};
};
.container {
font-size: 0.8rem;
}
.container table {
border-collapse: collapse;
}
.container table th,
.container table td {
border: #ccc solid 1px;
padding: 0.2rem 0.4rem;
width: 100px;
}
.result-table {
display: inline-block;
height: 230px;
overflow: auto;
}
<div class="container">
<form>
<input type="number" id="txtLimit" step="10" max="1000" min="0" value="10">
<input type="text" id="input" placeholder="what you are looking for?">
<button type="submit">Search</button>
</form>
</div>

assign JSON to variable and use $.getJSON
use below code with your search button,
$("#search").on('keypress keyup change input', function() {
var arrival = $(this).val().toLowerCase();
$('#matches').text(!arrival.length ? '' :
dataArr.filter(function(place) {
// look for the entry with a matching `code` value
return (place.title.toLowerCase().indexOf(arrival) !== -1);
}).map(function(place) {
// get titles of matches
return place.title;
}).join('\n')); // create one text with a line per matched title
});

Related

Javascript not working/loading properly after overlaying a new site over existing one

Basically I am trying to only give access to a certain page if a user has a certain item. After that is authenticated, the new page is only able to show basic HTML and CSS styling. The JavaScript does not seem to be loading at all... This is my index.html
const revealMsg = async () => {
var signature = await web3.eth.personal.sign("Message to sign", account)
console.log(signature)
var res = await fetch('/exclusive?signature=' + signature)
//console.log(res)
var body = await res.text()
//console.log(body);
if (body != 'FALSE') {
//console.log(body)
document.querySelector('html').innerHTML = body;
} else {
//alert you do not have item
alert("You do not have the item!");
}
}
This is my server.js
app.get('/exclusive', async (req, res) => {
isMember = false
var address = await web3.eth.accounts.recover("Message to sign", req.query.signature);
console.log(address)
var balanceOne = Number(await contractOne.methods.balanceOf(address).call())
var balanceTwo = Number(await contractTwo.methods.balanceOf(address).call())
console.log("Balance 1: ", balanceOne)
console.log("Balance 2: ", balanceTwo)
if(balanceOne > 0 || balanceTwo > 0){
isMember = true;
res.sendFile(path.join(__dirname, '/exclusive.html'))
}else{
res.send("FALSE")
}
})
And here is the exclusive page I am trying to test simple JS things
<!DOCTYPE html>
<html>
<head>
<title>Exclusive Page</title>
<script src="https://cdn.jsdelivr.net/npm/web3#latest/dist/web3.min.js"></script>
<style>
body {
background-color: red;
}
</style>
<h1>Hello World</h1>
<button onclick="clear()">Lost Tapes</button>
<button id="clear2">Clear2</button>
<script>
const clear = () => {
console.log('clear')
}
function clear2() {
console.log('clear2');
}
document.getElementById("clear2").onclick = clear2
</script>
</body>
</html>

Setting a value in html using buttons generated from an Apps Script / javascript array

I am trying to piece together a Google Apps script for a touchscreen clocking in system, and that involves generating some buttons from an array taken from a spreadsheet, but I'm getting a bit lost working out how to get the script to document which of these automatically generated buttons has been pressed.
Basically, I am using a modified version of this script (https://www.youtube.com/watch?v=KGhGWuTjJwc) - I need it to work with a touch screen.
I have got a version of the script given in the video above working. But because the web app must work on a touch screen I need to be able to generate buttons for the input of staff ID and PIN to replace the text boxes.
To do this I am pulling in an array from a Google Sheet that contains all staff initials needed to log in, as well as adding a numeric keypad to enter a PIN.
I have (sort of) successfully borrowed a keypad from here:https://github.com/ProgrammingHero1/batch5-pin-matcher-solution but I am stuck trying to get use buttons for the staff ID input.
I have managed to generate the buttons from the spreadsheet array, using a modified version of this Create an array of buttons from Javascript array but I can't work out how to set staffid to match whichever of the auto-generated buttons was pressed last. Currently those buttons don't do anything.
If anyone can help guide me the last step of the way so that staffid is set using the buttons and not using the text box that would be very much appreciated.
Below is the code I am using. I am aware that it is a dog's breakfast - that's because I am hacking it together from various sources working at (well, realistically well beyond) the limits of my understanding!
Anyway, here is the HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<!-- Metro 4 -->
<link rel="stylesheet" href="https://cdn.metroui.org.ua/v4/css/metro-all.min.css">
<style>
.main-box {
max-width:500px;
}
.error-message {
background:red;
color:white
padding: 1000px;
border-radius: 1000px;
}
</style>
</head>
<body>
<div class="container">
<div class="container d-flex flex-justify-center pt-9">
<div data-role="panel" class="main-box">
<form id="main-form">
<div class="form-group">
<input type="text" data-role="input" data-prepend="Staff ID" id="staffid">
<div class="form-group">
</div>
<div class="form-group">
<button id="start-work" class="button success" type="submit" data-action="Start work">Start work</button>
<button id="finish-work" class="button success" type="submit" data-action="Finish work">Finish work</button>
<button id="start-break" class="button success" type="submit" data-action="Start break">Start break</button>
<button id="finish-break" class="button success" type="submit" data-action="Finish break">Finish break</button>
<div class="input-section half-width">
<input id="typed-numbers" class="form-control" type="text">
<div class="numbers">
<div id="key-pad" class="calc-body">
<div class="calc-button-row">
<div class="button">7</div>
<div class="button">8</div>
<div class="button">9</div>
</div>
<div class="calc-button-row">
<div class="button">4</div>
<div class="button">5</div>
<div class="button">6</div>
</div>
<div class="calc-button-row">
<div class="button">1</div>
<div class="button">2</div>
<div class="button">3</div>
</div>
<div class="calc-button-row">
<div class="button"><</div>
<div class="button">0</div>
<div class="button">C</div>
</div>
</div>
</div>
</div>
</div>
</form>
<div id="message" class="d-none error-message mt4">
Error!!
</div>
</div>
</div>
</body>
<!-- Metro 4 -->
<script src="https://cdn.metroui.org.ua/v4/js/metro.min.js"></script>
<script>
const TSPwebapp = {}
TSPwebapp.onLoad = function(){
TSPwebapp.form = document.getElementById("main-form")
TSPwebapp.staffidInput = document.getElementById("staffid")
TSPwebapp.pinInput = document.getElementById("typed-numbers")
TSPwebapp.startWorkButton = document.getElementById("start-work")
TSPwebapp.finishWorkButton = document.getElementById("finish-work")
TSPwebapp.startBreakButton = document.getElementById("start-break")
TSPwebapp.finishBreakButton = document.getElementById("finish-break")
TSPwebapp.message = document.getElementById("message")
TSPwebapp.form.addEventListener("submit",TSPwebapp.onSubmit)
TSPwebapp.startWorkButton.addEventListener("click",TSPwebapp.startFinishWork)
TSPwebapp.finishWorkButton.addEventListener("click",TSPwebapp.startFinishWork)
TSPwebapp.startBreakButton.addEventListener("click",TSPwebapp.startFinishBreak)
TSPwebapp.finishBreakButton.addEventListener("click",TSPwebapp.startFinishBreak)
} // TSPwebapp.onLoad function
TSPwebapp.onSubmit= function(e){
e.preventDefault()
} // TSPwebapp.onSubmit function
TSPwebapp.startFinishWork = function(e){
const payload = {
staffid: TSPwebapp.staffidInput.value,
pin: TSPwebapp.pinInput.value,
action: e.target.dataset.action
}
google.script.run.withSuccessHandler(() => {
TSPwebapp.staffidInput.value = ""
TSPwebapp.pinInput.value = ""
} ).withFailureHandler(() => {
TSPwebapp.message.classList.remove("d-none")
setTimeout(() => {TSPwebapp.message.classList.add("d-none")},5000)
}).startFinishWork(payload)
} // TSPwebapp.startFinishWork function
TSPwebapp.startFinishBreak = function(e){
const payload = {
staffid: TSPwebapp.staffidInput.value,
pin: TSPwebapp.pinInput.value,
action: e.target.dataset.action
}
google.script.run.withSuccessHandler(() => {
TSPwebapp.staffidInput.value = ""
TSPwebapp.pinInput.value = ""
} ).withFailureHandler(() => {
TSPwebapp.message.classList.remove("d-none")
setTimeout(() => {TSPwebapp.message.classList.add("d-none")},5000)
}).startFinishBreak(payload)
} // TSPwebapp.startFinishBreak function
</script>
<script>
document.getElementById('key-pad').addEventListener('click', function (event) {
const number = event.target.innerText;
const calcInput = document.getElementById('typed-numbers');
if (isNaN(number)) {
if (number == 'C') {
calcInput.value = '';
}
}
else {
const previousNumber = calcInput.value;
const newNumber = previousNumber + number;
calcInput.value = newNumber;
}
});
</script>
<script>
document.addEventListener("DOMContentLoaded",TSPwebapp.onLoad)
document.addEventListener("DOMContentLoaded",function(){
google.script.run.withSuccessHandler(printBtn).useDataRange();
});
function printBtn(staffData) {
for (var i = 0; i < staffData.length; i++) {
var btn = document.createElement("button");
var t = document.createTextNode(staffData[i]);
btn.appendChild(t);
document.body.appendChild(btn);
}
}
window.onload = printBtn();
</script>
</body>
</html>
Here is the Google Apps Script code:
function doGet(e) {
Logger.log(e);
return HtmlService.createHtmlOutputFromFile("form");
}
function useDataRange () {
const ss = SpreadsheetApp.getActiveSpreadsheet()
const wsStaff = ss.getSheetByName("Staff")
const staffData = wsStaff.getRange(2,2,wsStaff.getLastRow()-1,1).getValues()
Logger.log(staffData)
return staffData;
}
function startFinishBreak(payload){
console.log(payload)
if (!["Start work","Finish work","Start break","Finish break"].includes(payload.action)){
throw new Error("Sign in or sign out failed")
return
}
console.log("Initial check passed")
const ss = SpreadsheetApp.getActiveSpreadsheet()
const wsBreakData = ss.getSheetByName("Break data")
const wsStaff = ss.getSheetByName("Staff")
const staffData = wsStaff.getRange(2,2,wsStaff.getLastRow()-1,3).getValues()
const matchingStaff = staffData.filter(r => r[0].toString() === payload.staffid && r[2].toString() === payload.pin)
if(matchingStaff.length !== 1){
throw new Error("Sign in or sign out failed")
return
}
const idsData = wsBreakData.getRange(2,2,wsBreakData.getLastRow()-1,3).getValues()
console.log(idsData)
const matchingIdsData = idsData.filter(r => r[0].toString() === payload.staffid)
console.log(matchingIdsData)
const latestAction = matchingIdsData.length === 0 ? "Finish break" : matchingIdsData[matchingIdsData.length-1][2]
if (latestAction === payload.action){
throw new Error("Sign in or sign out failed")
return
}
wsBreakData.appendRow([new Date(),payload.staffid,payload.pin,payload.action])
}
function startFinishWork(payload){
console.log(payload)
if (!["Start work","Finish work","Start break","Finish break"].includes(payload.action)){
throw new Error("Sign in or sign out failed")
return
}
console.log("Initial check passed")
const ss = SpreadsheetApp.getActiveSpreadsheet()
const wsWorkData = ss.getSheetByName("Work data")
const wsStaff = ss.getSheetByName("Staff")
const staffData = wsStaff.getRange(2,2,wsStaff.getLastRow()-1,3).getValues()
const matchingStaff = staffData.filter(r => r[0].toString() === payload.staffid && r[2].toString() === payload.pin)
if(matchingStaff.length !== 1){
throw new Error("Sign in or sign out failed")
return
}
const idsData = wsWorkData.getRange(2,2,wsWorkData.getLastRow()-1,3).getValues()
console.log(idsData)
const matchingIdsData = idsData.filter(r => r[0].toString() === payload.staffid)
console.log(matchingIdsData)
const latestAction = matchingIdsData.length === 0 ? "Finish work" : matchingIdsData[matchingIdsData.length-1][2]
if (latestAction === payload.action){
throw new Error("Sign in or sign out failed")
return
}
wsWorkData.appendRow([new Date(),payload.staffid,payload.pin,payload.action])
}
I hope this description all more or less makes sense - I am trying something a way beyond by current capabilities but I do need to get this task done and hopefully I'm learning in the process.
You need to add a value and event listener to each button. Then you can use the value to determine which button was pressed.
I have updated my script to address the comments below.
First add a new division.
<div id="staffButtons">
</div>
<div id="message" class="d-none error-message mt4">
Next make some changes to original script as shown.
function printBtn(staffData) {
let div = document.getElementById("staffButtons"); // add this
for (var i = 0; i < staffData.length; i++) {
var btn = document.createElement("button");
var t = document.createTextNode(staffData[i]);
btn.value = staffData[i]
btn.addEventListner("click",buttonClick);
btn.appendChild(t);
div.appendChild(btn); // change this
}
}
function buttonClick() {
document.getElementById("staffid") = this.value;
}
Reference
addEventListener

How to re-write the code for the following workflow , first upload a video and then process it? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 1 year ago.
Improve this question
First, I'm trying to change the behavior for uploading a video and then press the button "corta" and It will cut, Any Idea, please?
.
The current behavior is that it will cut when you change the data of the input file ( When you upload a video ) and It's not waiting for clicking button "corta".
That's the link of the functionality, thanks.
<html>
<head>
<script src="https://unpkg.com/#ffmpeg/ffmpeg#0.9.5/dist/ffmpeg.min.js"></script>
<style>
html,
body {
margin: 0;
width: 100%;
height: 100%
}
body {
display: flex;
flex-direction: column;
align-items: center;
}
</style>
</head>
<body>
<button onclick="myFunction()">corta</button>
<video id="output-video" controls></video><br />
<input type="file" id="uploader">
<p id="message"></p>
<script>
function myFunction() {
const { createFFmpeg, fetchFile } = FFmpeg;
const ffmpeg = createFFmpeg({ log: true });
const trim = async ({ target: { files } }) => {
const message = document.getElementById('message');
const { name } = files[0];
message.innerHTML = 'Loading ffmpeg-core.js';
await ffmpeg.load();
message.innerHTML = 'Start trimming';
ffmpeg.FS('writeFile', name, await fetchFile(files[0]));
await ffmpeg.run('-i', name, '-ss', '0', '-to', '1', 'output.mp4');
message.innerHTML = 'Complete trimming';
const data = ffmpeg.FS('readFile', 'output.mp4');
const video = document.getElementById('output-video');
video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' }));
}
const elm = document.getElementById('uploader');
elm.addEventListener('change', trim);
}
</script>
</body>
</html>
first off, your button on click myFunction does nothing but create a function and attach an event listener. you want
to move those out of the function.
Then you want to target the input element, and not just use the event.target since the event target will be different if someone clicks the button.
<html>
<head>
<script src="https://unpkg.com/#ffmpeg/ffmpeg#0.9.5/dist/ffmpeg.min.js"></script>
<style>
html,
body {
margin: 0;
width: 100%;
height: 100%
}
body {
display: flex;
flex-direction: column;
align-items: center;
}
</style>
</head>
<body>
<button id="corta">corta</button>
<video id="output-video" controls></video><br />
<input type="file" id="uploader">
<p id="message"></p>
<script>
(function() {
const { createFFmpeg, fetchFile } = FFmpeg;
const ffmpeg = createFFmpeg({ log: true });
const input = document.getElementById('uploader');
const corta = document.getElementById('corta');
const trim = async () => {
if (input.files.length > 0) {
const message = document.getElementById('message');
const { name } = input.files[0];
message.innerHTML = 'Loading ffmpeg-core.js';
await ffmpeg.load();
message.innerHTML = 'Start trimming';
ffmpeg.FS('writeFile', name, await fetchFile(input.files[0]));
await ffmpeg.run('-i', name, '-ss', '0', '-to', '1', 'output.mp4');
message.innerHTML = 'Complete trimming';
const data = ffmpeg.FS('readFile', 'output.mp4');
const video = document.getElementById('output-video');
video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' }));
}
}
corta.addEventListener('click', trim);
})(); // Self invoking as to not create global variables.
</script>
</body>
</html>

Searching for parks in one or more state

Im a new developer looking for help from someone with more experience. I'm trying to understand how to use a api/endpoint I created a key and now im trying to get my app to search for parks in one or more states and also display the full name, the description. Here's the api documentation. I hope I asked the question well enough I'm open to constructive criticism.
API (please copy and paste link in a new tab)
https://www.nps.gov/subjects/developer/api-documentation.htm#/parks/getPark
'use strict';
// put your own value below!
const apiKey = '';
const searchURL = 'https://developer.nps.gov/api/v1/parks?parkCode=acad&';
function formatQueryParams(params) {
const queryItems = Object.keys(params)
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
return queryItems.join('&');
}
function displayResults(responseJson) {
// if there are previous results, remove them
console.log(responseJson);
$('#results-list').empty();
// iterate through the items array
for (let i = 0; i < responseJson.items.length; i++){
// for each video object in the items
//array, add a list item to the results
//list with the video title, description,
//and thumbnail
$('#results-list').append(
`<li><h3>${responseJson.items[i].snippet.title}</h3>
<p>${responseJson.items[i].snippet.description}</p>
<img src='${responseJson.items[i].snippet.thumbnails.default.url}'>
</li>`
)};
//display the results section
$('#results').removeClass('hidden');
};
function getYouTubeVideos(query, maxResults=10) {
const params = {
key: apiKey,
q: query,
part: 'snippet',
maxResults,
type: 'video'
};
const queryString = formatQueryParams(params)
const url = searchURL + '?' + queryString;
console.log(url);
fetch(url)
.then(response => {
if (response.ok) {
return response.json();
}
throw new Error(response.statusText);
})
.then(responseJson => displayResults(responseJson))
.catch(err => {
$('#js-error-message').text(`Something went wrong: ${err.message}`);
});
}
function watchForm() {
$('form').submit(event => {
event.preventDefault();
const searchTerm = $('#js-search-term').val();
const maxResults = $('#js-max-results').val();
getYouTubeVideos(searchTerm, maxResults);
});
}
$(watchForm);
* {
box-sizing: border-box;
}
body {
font-family: 'Roboto', sans-serif;
}
button, input[type="text"] {
padding: 5px;
}
button:hover {
cursor: pointer;
}
.container {
max-width: 600px;
margin: 0 auto;
}
.error-message {
color: red;
}
.hidden {
display: none;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>YouTube video finder</title>
<link rel="shortcut icon" href="#">
<link rel="stylesheet" href="index.css">
</head>
<body>
<div class="container">
<h1>Park finder</h1>
<form id="js-form">
<label for="search-term">Search term</label>
<input type="text" name="search-term" id="js-search-term" required>
<label for="max-results">Maximum results to return</label>
<input type="number" name="max-results" id="js-max-results" value="10">
<input type="submit" value="Go!">
</form>
<p id="js-error-message" class="error-message"></p>
<section id="results" class="hidden">
<h2>Search results</h2>
<ul id="results-list">
</ul>
</section>
</div>
<script src="https://code.jquery.com/jquery-3.3.1.js" integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=" crossorigin="anonymous"></script>
<script src="index.js"></script>
</body>
</html>
'use strict';
// put your own value below!
const apiKey = 'XDkrghHigMG7xYtlfMloyKAoJ04H4te9h3UKWW3g';
const searchURL = 'https://developer.nps.gov/api/v1/parks?parkCode=acad&api_key=XDkrghHigMG7xYtlfMloyKAoJ04H4te9h3UKWW3g';
function formatQueryParams(params) {
const queryItems = Object.keys(params)
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
return queryItems.join('&');
}
function displayResults(responseJson) {
// if there are previous results, remove them
console.log(responseJson);
$('#results-list').empty();
// iterate through the items array
for (let i = 0; i < responseJson.items.length; i++){
// for each video object in the items
//array, add a list item to the results
//list with the video title, description,
//and thumbnail
$('#results-list').append(
`<li><h3>${responseJson.items[i].snippet.title}</h3>
<p>${responseJson.items[i].snippet.description}</p>
<img src='${responseJson.items[i].snippet.thumbnails.default.url}'>
</li>`
)};
//display the results section
$('#results').removeClass('hidden');
};
function getYouTubeVideos(query, maxResults=10) {
const params = {
key: apiKey,
q: query,
part: 'snippet',
maxResults,
type: 'video'
};
const queryString = formatQueryParams(params)
const url = searchURL + '?' + queryString;
console.log(url);
fetch(url)
.then(response => {
(responseJson => displayResults(responseJson))
if (response.ok) {
return response.json();
console.log(response.json());
}
throw new Error(response.statusText);
})
.catch(err => {
$('#js-error-message').text(`Something went wrong: ${err.message}`);
});
}
function watchForm() {
$('form').submit(event => {
event.preventDefault();
const searchTerm = $('#js-search-term').val();
const maxResults = $('#js-max-results').val();
getYouTubeVideos(searchTerm, maxResults);
});
}
$(watchForm);
I can see a few things going on and I have some debug suggestions. Also, I would recommend cleaning up the Javascript so that it doesn't have a YouTube reference...
When you fetch(url) the first .then returns the json result but your call of the function doesn't do anything with the result. Presumably you would call displayResults, but you're not doing that (yet?). So at least console log response.json to see what's happening
You're use of two . then is incorrect. The first returns from the function so the second is never called. Get rid of the then and move the code into the first then
Debug suggestions - when you're stuck on something it's best to break it down into small steps and check the behavior from beginning to end
did you generate the right query url? You have console logged it which is good. Copy the url and paste into browser. Are you getting good json back? If not, go through the url building process step by step. Which reminds me, make sure you use your api key, it's set to "" in your code
check that your code is returning the same json as you got from the browser, if not, check whether anything is different between the browser call and the fetch
this should have got you past the point where you are stuck. Next step is to check the display function, and so on

Trouble with Google charts, queries and javascript

I'm trying to create a very simple website consisting of a Google bubble chart overlaid on a background image. The bubble chart is supposed to be generated based on data from a set of Google spreadsheets, and the user can select, through a simple text form, the names of the data points that are to be published. In the code below I have obfuscated the spreadsheet URL, because I don't want to make the data public yet (note however that the viewing privileges of the document are set to public, as required).
The code doesn't work, as no chart is drawn. Obviously there is something fishy going on, because there is never an alert from the collectData method, which handles the query response.
Also, initially I didn't have the draw method and its code was instead in the sendAndDraw method, sans the setTimeout. In that version, I got a Data table not found error on the webpage, but again, the collectData method didn't seem to be called, as it didn't raise an error.
Any suggestions as to what might be the issue? I should add that I am completely new to both javascript and Google developers tools.
EDIT: Following kfa's comment, the form was changed to include a post method. Now I get the Data Table not defined problem once again.
<!DOCTYPE html>
<html>
<head>
<style>
#container{
position: relative;
width: 200px;
height: 200px;
}
#background{
width: 100%;
height: 100%;
}
#bubbleChart{
width: 100%;
height: 100%;
}
</style>
<title>gRNA</title>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
google.charts.load('current', {'packages' : ['corechart']});
google.charts.setOnLoadCallback(function() {initialize()});
var dataSourceUrls = [
'https://docs.google.com/spreadsheets/d/foo/edit#gid=0&headers=1'
];
var nSheets = dataSourceUrls.length;
var query;
var bubbleDataArray = [];
var bubbleData;
function initialize() {
//Currently not doing anything here
}
//Takes a list of names as a comma separated list.
function sendQueries(nameString) {
var queryString = generateQuery(nameString);
for(i = 0; i < nSheets; i++) {
query = new google.visualization.Query(dataSourceUrls[i]);
query.setQuery(queryString);
query.send(collectData);
query.abort();
}
}
//Generates the query string for the selected names.
function generateQuery(nameString) {
nameString = nameString.split(",");
var nNames = nameString.length;
var queryString = [];
queryString.push("select F, D, E, B ");
queryString.push("where F matches ");
queryString.push(nameString[0]);
for(i = 1; i < nNames; i++) {
queryString.push("or ");
queryString.push(nameString[i]);
}
return queryString.join("");
}
//Collect and manage the query responses.
function collectData(response) {
alert('Hi!');
if(response.isError()) {
alert('Error in query: ' + response.getMessage() + ' ' +
response.getDetailedMessage());
return;
}
var data = response.getDataTable();
if(data.getNumberOfRows() > 0) {
bubbleDataArray.push(data);
}
}
function sendAndDraw(nameString) {
bubbleDataArray = [];
sendQueries(nameString);
setTimeout(draw,2000);
}
function draw() {
bubbleData = bubbleDataArray[0];
var nTables = bubbleDataArray.length;
for(i = 1; i < nTables; i++) {
bubbleData = google.visualization.data.join(bubbleData,
bubbleDataArray[i], 'full', [[0,0]],
[1,2,3], [1,2,3]);
}
var container = document.getElementById('bubbleChart');
var bubbleChart = new google.visualization.BubbleChart(container);
var options = {
'backgroundColor':'transparent'
}
bubbleChart.draw(bubbleData,options);
}
function plot() {
sendAndDraw(document.getElementById('nameSel').value);
}
</script>
</head>
<body>
<form onsubmit="plot(); return false;" method="post">
<input type="text" id="nameSel"/>
<input type="submit" value="Plot"/>
</form>
<br />
<div id="container">
<div id="background"></div>
<div id="bubbleChart"></div>
</div>
</body>
</html>

Categories