search-bar for JSON (API), need help:) - javascript

i am currently stuck trying to code a search-bar for an JSON fil (linked via an API link and key). I am fairly new to coding and especially new to this field.
i currently cant get my function to work with onkeyup() maybe because its an async function? as i said im fairly new to this so idk, and I get an error for my "x.innerHTML = "" " saying "Cannot set properties of null", now i may look like an idiot here and missing something obvious but I would very much appreciate all answers:)
thanku for your time!
HTML:
<div id="søk"><input id="søkefelt" onkeyup="søk()" type="text" placeholder="Search..">
<ul id="listHolder"></ul>
</div>
javascript:
window.addEventListener("DOMContentLoaded", (Event) => {
async function søk() {
const apiKey = await fetch ("nyheter.json")
.then ((response) => response.json());
var data = await fetch ("https://newsapi.org/v2/top-headlines?country=us&category=business&apiKey=" + apiKey[0].apikey)
.then((response) => response.json());
let input = document.getElementById("søkefelt").value
input = input.toLowerCase();
let x = document.querySelector("listHolder");
x.innerHTML = ""
for (let i = 0; i < data.length; i++) {
const obj = data[i];
if (obj.title.toLowerCase().includes(input)) {
const elem = document.createElement("li")
elem.innerHTML = '${obj.title} - ${author}'
x.appendChild(elem)
}
};
}
søk();
});

querySelector requires # before ids
querySelector("#listHolder")

Related

How to populate without using mongoose

I'm trying to implement populate() function without using mongoose in the code below:
`
course.students.forEach(async (student, i) => {
const s = await Student.findById(student._id);
console.log(s.toObject()); // gets student data properly
course.students[i] = s; // does not work
});
console.log(course.json());
`
I just want to update the students array with the data fetched. Can anyone explain me with the assignation doesn't works?
Thank you all! :)
forEach is not meant to be used with await, try to change to a for loop and use lean() to return a plain object from the query:
for (let i = 0; i < course.students.length; i++) {
const student = course.students[i];
course.students[i] = await Student.findById(student._id).lean();
}
console.log(course);

How to render RapidAPI data on another HTML page?

I am new to JavaScript and this is my first question here. I've been trying for week to render my RapidApi data on another HTML page. I made search form on my index page and then put its values as my api call parameters in order to influence my API response. I used fetch to do so. The issue is that my API data keeps rendering on the same index page which is understandable since I don't know how to render it on a separate page. This also means that my CSS styling options are limited since I cannot design API data as I want without messing up my index page. If you have any sort of solution that is not way too complicated I would really appreciate your help.
Here is part of my code:
const input = document.getElementById(`location`);
const guests = document.getElementById(`guests`);
const check = document.querySelectorAll(".date");
let id;
document.getElementById(`submit`).addEventListener(`click`, function (e) {
e.preventDefault();
locationId();
});
async function locationId () {
let hotelId = input.value;
const options = {
method: 'GET',
headers: {
'X-RapidAPI-Key': '//API key goes here',
'X-RapidAPI-Host': 'tripadvisor16.p.rapidapi.com'
}
};
let response = await fetch(`https://tripadvisor16.p.rapidapi.com/api/v1/hotels/searchLocation?query=${hotelId}`, options);
if (!response.ok) throw new Error(`Woops something went wrong`);
let data = await response.json();
let geoId = await (data.data[0].geoId);
id= parseInt(geoId);
return (fetch(`https://tripadvisor16.p.rapidapi.com/api/v1/hotels/searchHotels?geoId=${id}&checkIn=${check[0].value}&checkOut=${check[1].value}&pageNumber=1&adults=${guests.value}currencyCode=USD`, options))
.then(response => response.json())
.then(data => {
let list = data.data.data;
displayObjectElements(list)
function displayObjectElements (object) {
let display = ``;
let price = ``;
object.forEach(element => {
display+= `<div class = "objectResults">
<ul class="hotel__lists">
<li><h2 class = "title">${element.title}</h2></li>
<li><img class= "hotels--photo "src="${element.cardPhotos[0].sizes.urlTemplate.split("?")[0] + `?w=500&h=500`}" alt=image--photo/></li>
<li><p>Ranking:${element.bubbleRating.rating}&#9734 out of 5&#9734</p></li>`
if(!element.priceForDisplay) {
display+= `<li><p>There is no price to display</p></li>`
display+= `<li><button class="booking-btn">Click to book</button></li>`
} else {
price =element.priceForDisplay.substring(1);
price= parseInt(price);
// console.log(price);
display+= `<li><p>Price: $${price} in total</p></li>`
display+= `<li><button class = "booking-btn">Click to book</button></li>
</ul>
</div>`
// console.log(display);
}});
document.body.innerHTML = display;
}
})
.catch(err => console.error(err));
}
I already tried with localStorage and sessionStorage but as a newbie I am just now sure how to put the whole API data in storage. Also, I desperately tried with window.location object as well but as I assumed that did nothing but open a new tab. Again, thanks in advance for any help!

SetTimeout is confusing me and I need to be able to promisify it

I have 2 separate div elements and I have a function that defines each element and is supposed to only act on the element. so when i call typeField(name,element1) it should type into only element1 and similarly then I call typeField(address,element2) it should type into only element2.. but currently if you check my codepen implementation it's mixing the characters and I am not sure why.
Also after this I need to figure a way out where I can get the name to type in first and then after that's done I need to start the typing of the address. My assumption is I need to promisify the functions and then call them as async/await. Is that the right approach? or am I overthinking this?
So my 2 questions are
What am I doing wrong in my code that's mixing up the contents in elements innerHTML?
Once that's resolved how can I make the first element type first and on completion start the next one
Any help in resolving this would be appreciated.
This is the link to my Codepen
https://codepen.io/alimbolar/pen/Rwjoqbm?editors=1111
const typewriter1 = document.getElementById('typewriter1');
const typewriter2 = document.getElementById('typewriter2');
const name = 'Alim Bolar';
const address = "Some part of the world";
const currentLocation = "Bali, Indonesia";
const profile = "Frontend Development, Backend Development, WordPress Development"
let content = "";
let i = 0;
function typeField(x, element) {
if (i < x.length) {
alphabet = x.charAt(i);
content = content + alphabet;
element.innerHTML = content;
i++;
setTimeout(function() {
typeField(x, element)
}, 500);
}
}
typeField(name, typewriter1);
typeField(address, typewriter2);
<header>
<div>NAME : <span id="typewriter1"></span></div>
<div>ADDRESS : <span id="typewriter2"></span></div>
</header>
You need to
not have i or content be global, because then it'll be shared by all calls of typeField
have typeField assign to a persistent Promise outside that further calls are chained off of
Consider awaiting a Promise inside a loop, it'll look a lot cleaner than recursion.
const typewriter1 = document.getElementById('typewriter1');
const typewriter2 = document.getElementById('typewriter2');
const name = 'Alim Bolar';
const address = "Some part of the world";
const currentLocation = "Bali, Indonesia";
const profile = "Frontend Development, Backend Development, WordPress Development"
let prom = Promise.resolve();
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
function typeField(textToInsert, element) {
prom = prom.then(async () => {
for (const char of textToInsert) {
element.textContent += char;
await delay(500);
}
});
}
typeField(name, typewriter1);
typeField(address, typewriter2);
<header>
<div>NAME : <span id="typewriter1"></span></div>
<div>ADDRESS : <span id="typewriter2"></span></div>
</header>

Google Apps Script withSuccessHandler() doesn't work as expected

I'm learning Apps Script and I'm getting very frustrated because I can't see what is wrong with my approach.
According to the docs, when running google.script.run.withSuccessHandler(successFunc).myFunction() from within an html file script, whatever myFunction returns should be available as a parameter for successFunc, or shouldn't it? I'm dubious now because I can't seem to make it work.
Here's my HTML:
<body>
<form>
<label for="fn">Nombre</label>
<input id="fn" name="fn" type="text">
<label for="ln">Apellido/s</label>
<input id="ln" name="ln" type="text">
<button type="button" id="search">Buscar</button>
<label for="found">Resultado</label>
<input type="text" name="found" id="found" disabled>
<button type="submit" id="setClient">Continuar</button>
</form>
<script>
const fn = document.getElementById('fn').value;
const ln = document.getElementById('ln').value;
const search = document.getElementById('search');
const found = document.getElementById('found');
const setClient = document.getElementById('setClient');
let clientId; // The ID of the client in the database;
let data;
search.addEventListener("click", () => {
google.script.run.withSuccessHandler(addClient).searchDb(fn, ln);
});
function addClient(data) {
alert(data); // returns null
if (!data || data === null) throw "No data found"; // don't care about gracefully catching this right now
found.value = `${data.clientFn}, ${data.clientLn}`; // adds 'undefined, undefined'
clientId = data.clientId;
// google.script.host.close();
}
setClient.addEventListener("click", ()=>{
google.script.run.setClient(fn, ln, clientId);
})
</script>
</body>
As you can see, I've added an "alert" in the success handler just to keep track of data and even though I have tested the code ad infinitum and it always return a fully workable object, this seems to always show null.
Here's also my .gs function:
function searchDb(fn, ln) {
const ws = SpreadsheetApp.openById("the ID of the SS, obviously").getSheets()[0];
const dataRange = ws.getRange(2, 1, ws.getLastRow() - 1, 3).getValues();
const lnRange = dataRange.map(r => { return r[2] });
const fnRange = dataRange.map(r => { return r[1] });
const idRange = dataRange.map(r => { return r[0] });
for (let a = 0; a < lnRange.length; a++) {
const clientLn = lnRange[a];
const clientFn = fnRange[a];
const clientId = idRange[a];
if (clientLn.includes(ln) && clientFn === fn) {
const data = {
clientFn: clientFn,
clientLn: clientLn,
clientId: clientId
}
return data;
}
}
return;
}
It is, indeed, a very simple program and it does return the data object with the appropriate data when I test it, so not sure what's going on here.
I'm pretty sure I'm missing something pretty obvious, but I got to the point where I'm just banging my head against the wall...
Any help will be appreciated!
EDIT
I've edited the code because what I originally posted wasn't consistent - and the result of not reverting back completely from trying other approaches -. Still, the problem remains.
This is not an array:
const data = {'clientFn': clientFn,'clientLn': clientLn,'clientId': clientId}
But your treating it as an array:
found.value = `${data[1]}, ${data[0]}`;
clientId = data[2];
Try referring to them with their properties 'clientFn','clientLn','clientId'
found.value = `${data['clientLn']}, ${data['clientFn']}`;
clientId = data['clientId'];
Also you may want to refer to this link to insure that they are all legal types.
It turns out the problem was not to do with withSuccessHandler, as I expected, but with how the data was passed on to searchDb because the vars fn and ln haven't changed value as far as the script is concerned when the search button is clicked. The solution is as simple as declaring those variables after the search button has been clicked:
<script>
const search = document.getElementById('search');
const fullName = document.getElementById('fullName');
const country = document.getElementById('country');
const email = document.getElementById('email');
const noClient = document.getElementById('noClient');
const useClient = document.getElementById('useClient');
let clientId; // The ID of the client in the database;
search.addEventListener("click", () => {
const fn = document.getElementById('fn').value;
const ln = document.getElementById('ln').value;
google.script.run.withSuccessHandler(addClient).searchDb(fn, ln);
});
// ...etc

Routing function not being called

I am working on a simple restaurant web app which uses a mongo-db database to store the menu items. My issue is that I have a client js file that will use a routing function that then accesses the database to return all the menu items of a certain restaurant. My issue is that my endpoint for the url isn't being recognized:
Client.js
function readMenu(rest){
(async () => {
// const newURL = url + "/menus/"+rest
const resp = await fetch(url+"/menus/"+rest)
const j = await resp.json();
itemlist = j["items"]
var element = document.getElementById("menu")
var i;
for (i = 0; i < itemlist.length; i++) {
var para = document.createElement("p")
item = itemList[i]
text = item["name"]+" | "+item["cost"]+" | "+item["descr"] +"<br>";
var node = document.createTextNode(text)
para.appendChild(node)
element.appendChild(para)
}
})
}
Server-routing.ts (Routings):
this.router.get("/menus", this.getResturants.bind(this))
this.router.post("/menus", this.addResturaunt.bind(this))
this.router.get("/menus/:rest", this.getResturauntItems.bind(this))
this.router.delete("/menus/:rest",this.deleteResturaunt.bind(this))
this.router.get("/menus/:rest/:item",[this.errorHandler.bind(this),this.getItem.bind(this)])
this.router.post("/menus/:rest",this.addItem.bind(this))
this.router.delete("/menus/:rest/:item",this.deleteItem.bind(this))
Server-routing.ts (function):
public async getResturauntItems(request, response) : Promise<void> {
console.log("Getting Restaurant Items")
let rest = request.params.rest
let obj = await this.theDatabase.getResturauntItems(rest)
console.log(obj)
response.status(201).send(JSON.stringify(obj))
response.end()
}
So, what should happen is a button calls readMenu(), it then makes a GET fetch request to localhost:8080/api/menus/ and then the menu items from the collection should be returned. The issue is that when I click the button, nothing happens. I know it is not being redirected to some other function as they all have "console.log()" to keep track of them and none of them where called. I used the "inspect" tool to see if the request was being sent or received anywhere and nothing. I am unsure of what the issue happens to be. If anyone can help, it would be really appreciated.
you just never called your function, you declared the async function inside your function but never called it.
function readMenu(rest){
(async () => {
// const newURL = url + "/menus/"+rest
const resp = await fetch(url+"/menus/"+rest)
const j = await resp.json();
itemlist = j["items"]
var element = document.getElementById("menu")
var i;
for (i = 0; i < itemlist.length; i++) {
var para = document.createElement("p")
item = itemList[i]
text = item["name"]+" | "+item["cost"]+" | "+item["descr"] +"<br>";
var node = document.createTextNode(text)
para.appendChild(node)
element.appendChild(para)
}
})();
}
you need to add () after creating the functions to call it.

Categories