Searching for parks in one or more state - javascript

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

Related

Searching from JSON

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
});

How do i take input from user and search api?

How can I take input (page number) from the user and then look for the corresponding text that has that page number and display it for the user using this API = https://api.alquran.cloud/v1/page/${number}/quran-uthmani
If I clearly understand you, then you need this:
async function getVersesforpage(number) { //to get verses for a certain pagetry
try {
const url = `https://api.alquran.cloud/v1/page/${number}/quran-uthmani`;
const response = await fetch(url);
const data = await response.json();
const ayahs = data.data.ayahs;
const verses = ayahs.map(ayah => ayah.text);
return verses;
} catch (e) {
console.log("Error:", e.message)
}
}
const form = document.getElementById("form");
form.addEventListener("submit", async(event) => {
event.preventDefault();
const {
number
} = event.target.elements;
const value = number.value;
const verses = await getVersesforpage(value);
console.log(verses);
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>بصائر</title>
<link href="css/stylesheet1.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<link href="css/img/logo1.png" rel="icon">
<script type="module"></script>
</head>
<body>
<header class="header">
<form id="form">
<label for="number">رقم الصفحة</label>
<input id="number" placeholder="Search.." type="number">
<button class="fa fa-search" type="submit"></button>
</form>
</header>
<!--<script src="https://unpkg.com/localbase/dist/localbase.dev.js"></script>-->
<script src="js/main.js"></script>
</body>
</html>
You can get the user value from any field by filed id using document.getElementById('idName').value.
I prefered to edit you code a bit now the below code returns the result from API.
async function getVersesforpage(pageNumber) {//to get verses for a certain pagetry
try {
const url = `https://api.alquran.cloud/v1/page/${pageNumber}/quran-uthmani`;
const response = await fetch(url);
const verses = await response.json();
return verses;
}
catch (e) {
console.log("Error:", e.message)
}
}
document.getElementById("form").addEventListener("submit", async (event) => {
event.preventDefault();
const pageNumber = document.getElementById('number').value;
const verses = await getVersesforpage(pageNumber);
console.log(verses.data);
});

Fetch API Not displaying Images properly - TV Show Website

I'm a beginner when it comes to coding and the biggest issue I have is to understand WHY something doesn't work (how to diagnose an error). I tried to combine what I learned from Colt Steele on Udemy with fetch API and so far, I've managed to make it work to list the NAMES of the movies when you search, but when I try to display the IMAGES, they seem to not work and it seems like it's trying to load them from my PC rather than from the TVMaze API. Here's my code:
function searchShow(query) {
const url = `https://api.tvmaze.com/search/shows?q=${query}`;
fetch(url)
.then(response => response.json())
.then((jsonData) => {
const resultsNames = jsonData.map(element => element.show.name);
const resultsImages = jsonData.map(e => e.show.image);
console.log(resultsNames);
renderResults(resultsNames);
console.log(resultsImages);
renderImages(resultsImages);
document.getElementById("errorMessage").innerHTML = "";
})
.catch((error) => {
document.getElementById("errorMessage").innerHTML = error;
})
}
function renderResults(resultsNames) {
const list = document.getElementById("resultsList");
list.innerHTML = "";
resultsNames.forEach(result => {
const element = document.createElement("li");
element.innerText = result;
list.appendChild(element);
});
}
function renderImages(resultsImages) {
const list2 = document.getElementById("imagesDisplay");
list2.innerHTML = "";
resultsImages.forEach(result => {
const imgShow = document.createElement("IMG");
imgShow.src = result;
list2.appendChild(imgShow);
})
}
let searchTimeoutToken = 0;
window.onload = () => {
const searchFieldElement = document.getElementById("searchField")
searchFieldElement.onkeyup = (event) => {
clearTimeout(searchTimeoutToken);
searchTimeoutToken = setTimeout(() => {
searchShow(searchFieldElement.value);
}, 250);
};
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TV Show</title>
</head>
<body>
<h1>TV Search</h1>
<input type="text" id="searchField" placeholder="Search a TV Show...">
<ul id="resultsList"></ul>
<ul id="imagesDisplay"></ul>
<div id=" errorMessage">
</div>
<script src="script.js"></script>
</body>
</html>
Can you please help me understand why is this not working and also, how can I make it display in a list like this:
-Name of the show
-Image of the show
-2nd name of the 2nd show
-2nd image of the 2nd show
etc.
Thank you in advance!
If you look at your images, you will see the src as [object Object] instead of the url to your image. You need to access the property of your object, in this case there's a few to choose from that represent different sized images.
I've modified your snippet to get what you want.
function searchShow(query) {
const url = `https://api.tvmaze.com/search/shows?q=${query}`;
fetch(url)
.then(response => response.json())
.then((jsonData) => {
let shows = jsonData.map(element => element.show);
renderShows(shows);
document.getElementById("errorMessage").innerHTML = "";
})
.catch((error) => {
document.getElementById("errorMessage").innerHTML = error;
})
}
function renderShows(shows) {
const list = document.getElementById("resultsList");
list.innerHTML = "";
shows.forEach(show => {
const element = document.createElement("li");
const img = document.createElement("img");
const text = document.createElement("span");
img.src = show.image.original;
text.innerText = show.name;
element.appendChild(text);
element.appendChild(img);
list.appendChild(element);
});
}
let searchTimeoutToken = 0;
window.onload = () => {
const searchFieldElement = document.getElementById("searchField")
searchFieldElement.onkeyup = (event) => {
clearTimeout(searchTimeoutToken);
searchTimeoutToken = setTimeout(() => {
searchShow(searchFieldElement.value);
}, 250);
};
}
img {
max-width: 100px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TV Show</title>
</head>
<body>
<h1>TV Search</h1>
<input type="text" id="searchField" placeholder="Search a TV Show...">
<ul id="resultsList"></ul>
<ul id="imagesDisplay"></ul>
<div id="errorMessage">
</div>
<script src="script.js"></script>
</body>
</html>

change pagination number when scroll in url javascript

I have a problem. I want to make page like this
https://www.idntimes.com/korea/kpop/matthew-suharsono/rekomendasi-lagu-dreamcatcher-untuk-pengantar-tidur-c1c2/5
I already can add the page number at the end of the URL. But when I'm in testing.html/4 and I want to refresh it, the page does not appear and shows the error "Cannot get testing.html/4". How to make it can refresh like usual?
Here's my code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<style>
.spinner {
display: none;
}
</style>
</head>
<body style="font-size: 60px;">
<div class="news-content">
</div>
<div class="loading">
<p>Loading Please Wait</p>
</div>
<script>
function loadData(count) {
fetch('/index.json')
.then(res => res.json())
.then(json => {
if (count < json.length) {
let text = document.createElement('p');
text.innerText = json[count].text;
document.querySelector('.news-content').append(text);
if (count > 0) {
history.pushState(null, null, `/testing.html/${count}`)
}
}
});
}
let count = 0
window.addEventListener('load', loadData(count));
window.addEventListener('scroll', () => {
if ((window.innerHeight + window.pageYOffset) >= document.body.offsetHeight) {
count += 1;
loadData(count)
}
})
</script>
</body>
</html>
It seems to me that you are using pure HTML files in an HTTP/HTTPS local server. When you are having this kind of instance of the server you are not dynamically generating pages because you don't have any server side setup behind the HTML file.
You can do this using queries and since your app doesn't contain any server backend use client Javascript to create a pagination concept.
Instead of having a route type system ( which is usually handled by controller on the backend ) use query system:
Instead of:
/testing.html/{PAGE_NUMBER}
Use:
/testing.html?page={PAGE_NUMBER}
To get page query in Javascript, use the following function:
function getPageNumber() {
const urlParams = new URLSearchParams(window.location.search);
const page = urlParams.get('page');
return page;
}
Then create a function where you would paginate the data ( assuming the data is an array ):
function paginateData(data, resultsPerPage, pageNumber) {
// Chunk the data based on the limit
let result = data.reduce((rows, key, index) => (index % resultsPerPage == 0 ? rows.push([key]) : rows[rows.length-1].push(key)) && rows, []);
// Return the current page with index calculation
return result[pageNumber - 1];
}
And the final code should be something like this:
function getData(data) {
const RESULTS_PER_PAGE = 2;
const currentPageNumber = Number(getPageNumber());
const paginatedData = paginateData(data, RESULTS_PER_PAGE, currentPageNumber);
// If paginated data is undefined return first page
if (!paginatedData) {
/*
You can even redirect to /testing.html?page=1
*/
return paginateData(data, RESULTS_PER_PAGE, 1);
}
return paginatedData;
}
All you are left with is to provide the function getData with an data parameter resembling an array type.

How to remove an element from a separate JSON file using just that elements ID?

So I'm new to JS and none of the solutions I have found online have worked for me. I am creating a backend for a todolist, and I want to add the app.delete functionality so I can delete items from the list.
Here is the code in my separate json file:
[{"id":1,"name":"item 1","complete":true},{"id":2,"name":"Walk the dog","complete":false},{"id":3,"name":"Go shopping","complete":false}]
Here is what I tried, but to be honest I just wrote whatever came to mind for this:
app.delete('/todo/:id/delete', (request, response) => {
const id = request.params.id
const findTodobyID = (todos, id) => {
for(let i = 0; i < todos.length; i++){
if(todos[i].id === parseInt(id)){
return i
}
}
return -1
}
fs.readFile('./store/todos.json', 'utf-8', (err, data) => {
if(err) {
return response.status(500).send('Sorry, something went wrong.')
}
let todos = JSON.parse(data)
const todoIndex = findTodobyID(todos, id)
if (todoIndex === -1) {
return response.status(404).send('Sorry, ID not found')
}
todos[todoIndex].complete = true
fs.writeFile('./store/todos.json', JSON.delete(todos), () => {
return response.json({'status': 'Deleted ID' + id})
})
})
})
This is the error I get when I run it in Postman:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Cannot DELETE /todos/3/delete</pre>
</body>
</html>
If you want to remove the todoIndexth element from an array:
todos.splice(todoIndex, 1);
Also before writing it, you should use:
JSON.stringify(todos);
Instead of JSON.delete(todos).

Categories