Multiple fetches with eventListener - javascript

Hello there I am struggling to find the solution of this 'bug'. I am not even sure why its happening? Using Giphy API the goal is to upload gif then save the id from response to the localStorage.The initial upload seem to work fine, however each next upload does a multiple fetches and adds in the localStorage more than one id for each gif. Will really appreciate any advice. Thanks!
HTML:
<!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>
</head>
<body>
<form>
<input type="file" />
<input type="submit" />
</form>
<div class="tree"></div>
<script src="./fetch-api.js"></script>
</body>
</html>
JavaScript:
const form = document.querySelector('form');
const inputFlie = document.querySelector('input');
const preview = document.querySelector('.tree');
const apiKey = 'yourapikeyhere'
form.addEventListener('change', () => {
const uploadFile = new FormData();
uploadFile.append('file', inputFlie.files[0]);
const heads = {
method: 'POST',
api_key: apiKey ,
body: uploadFile,
};
form.addEventListener('submit', async (event) => {
event.preventDefault();
try {
const send = await fetch(
`https://upload.giphy.com/v1/gifs?api_key=${apiKey}`,
heads
);
const feedback = await send.json();
if (feedback.meta.status === 200) {
form.reset();
uploadID = feedback.data.id;
}
if (localStorage.getItem('uploaded') === null) {
//if we don't create an empty array
uploadedGifs = [];
uploadedGifs.push(uploadID);
localStorage.setItem('uploaded', JSON.stringify(uploadedGifs));
} else {
const currentItems = JSON.parse(localStorage.getItem('uploaded'));
currentItems.push(uploadID);
localStorage.setItem('uploaded', JSON.stringify(currentItems));
}
console.log(feedback);
} catch (error) {
console.log(error);
statusMesage.textContent = 'Something went wrong!';
}
});
});

separate event listeners, so as not to create a new one every time the form has been changed.
const form = document.querySelector('form');
const inputFlie = document.querySelector('input');
const preview = document.querySelector('.tree');
const apiKey = 'yourapikeyhere'
const heads = {
method: 'POST',
api_key: apiKey,
body: null,
};
form.addEventListener('change', () => {
const uploadFile = new FormData();
uploadFile.append('file', inputFlie.files[0]);
heads.body = uploadFile;
});
form.addEventListener('submit', async (event) => {
event.preventDefault();
try {
const send = await fetch(
`https://upload.giphy.com/v1/gifs?api_key=${apiKey}`,
heads
);
const feedback = await send.json();
if (feedback.meta.status === 200) {
form.reset();
uploadID = feedback.data.id;
}
if (localStorage.getItem('uploaded') === null) {
//if we don't create an empty array
uploadedGifs = [];
uploadedGifs.push(uploadID);
localStorage.setItem('uploaded', JSON.stringify(uploadedGifs));
} else {
const currentItems = JSON.parse(localStorage.getItem('uploaded'));
currentItems.push(uploadID);
localStorage.setItem('uploaded', JSON.stringify(currentItems));
}
console.log(feedback);
} catch (error) {
console.log(error);
statusMesage.textContent = 'Something went wrong!';
}
});

Related

headers.append('Content-Type', 'multipart/form-data') fails file upload in asp.net core 7

I am trying to understand the following problem, for what I created a minimal test application:
A html file:
<!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>Document</title>
</head>
<body>
<button title="Click me" value="Click me" onclick="onClickHandler()" />
<script type="text/javascript">
async function onClickHandler() {
let input = document.createElement('input');
input.type = 'file';
input.onchange = async (_) => {
if(!input.files || input.files.length === 0) return;
const result = await postFileAsync(input.files[0]);
};
input.click();
}
async function postFileAsync(file)
{
const headers = new Headers();
//headers.append('Content-Type', 'multipart/form-data');
const formData = new FormData();
formData.append("file", file);
const endpointUrl = "https://localhost:5001/v1/upload";
const result = await fetch(endpointUrl, {
method: 'POST',
cache: 'no-cache',
headers: headers,
body: formData
});
if (result.ok) {
try {
return { data: await result.json(), status: result.status };
} catch (error) {
return { status: result.status };
}
}
return { status: result.status };
}
</script>
</body>
</html>
A minimal api in asp.net core 7:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.MapPost("v1/upload", async ([FromForm]IFormFile file) =>
{
string tempfile = CreateTempfilePath();
using var stream = File.OpenWrite(tempfile);
await file.CopyToAsync(stream);
// dom more fancy stuff with the IFormFile
});
app.MapPost("v2/stream", async (Stream body) =>
{
string tempfile = CreateTempfilePath();
using var stream = File.OpenWrite(tempfile);
await body.CopyToAsync(stream);
});
app.MapPost("v3/stream", async (PipeReader body) =>
{
string tempfile = CreateTempfilePath();
using var stream = File.OpenWrite(tempfile);
await body.CopyToAsync(stream);
});
app.MapPost("/uploadmany", async (IFormFileCollection myFiles) =>
{
foreach (var file in myFiles)
{
string tempfile = CreateTempfilePath();
using var stream = File.OpenWrite(tempfile);
await file.CopyToAsync(stream);
// dom more fancy stuff with the IFormFile
}
});
string CreateTempfilePath()
{
var filename = $"{Guid.NewGuid()}.tmp";
var directoryPath = Path.Combine("temp", "uploads");
if (!Directory.Exists(directoryPath)) Directory.CreateDirectory(directoryPath);
return Path.Combine(directoryPath, filename);
}
app.Run();
Now what I do not understand, if I comment out in the js code the following code:
headers.append('Content-Type', 'multipart/form-data');
it never hit's the controllers v1/upload endpoint, if I comment out it hits.
Or what would be a best solution?
thnx
After test your code, I found the payload like below.
With
headers.append('Content-Type', 'multipart/form-data');
it didn't contain boundary.
So I did some searching in this direction and found a very good explanation in this blog, you can refer to it.
Uploading files using 'fetch' and 'FormData'

The github profile API data is undefined in JavaScript

I already check again on this code, but still couldn't figure it out why it won't work. So that I manage to make this web app using GitHub API.
but when I tried to search some data by their name, it turns out 'undefined' for everything that I was trying to find, like name, image, bio and etc.
My html code:
<html>
<head>
<title>Github Profile!</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
</head>
<body>
<form id="form">
<input type="text"
id="search"
placeholder="Search a User Here" />
</form>
<main id="main"></main>
<script src="script.js" defer></script>
</body>
</html>
Javascript:
const APIURL = 'https://api.github.com/users';
const main = document.getElementById('main');
const form = document.getElementById('form');
const search = document.getElementById('search');
async function getUser(user) {
const resp = await fetch(APIURL + user );
const respData = await resp.json();
createUserCard(respData);
}
function createUserCard(user) {
const cardHTML = `
<div class="card">
<div>
<img src="${user.avatar_url}"
alt="${user.name}" />
</div>
<div>
<h2>${user.name}</h2>
<p>${user.bio}</p>
<ul>
<li>${user.followers}</li>
<li>${user.following}</li>
<li>${user.public_repos}</li>
</ul>
</div>
</div>
`;
main.innerHTML = cardHTML;
}
form.addEventListener('submit', (e) => {
e.preventDefault();
const user = search.value;
if (user) {
getUser(user);
search.value = "";
}
});
I don't know what actually went wrong here.
Looks like you were just using the wrong URL.
const APIURL = 'https://api.github.com/users'; // no end slash
async function getUser(user) {
const resp = await fetch(APIURL + user );
so what you're doing here is calling the URL
https://api.github.com/usersusername
so you just need to add a slash in the APIURL variable.
const APIURL = 'https://api.github.com/users/';
const main = document.getElementById('main');
const form = document.getElementById('form');
const search = document.getElementById('search');
async function getUser(user) {
const resp = await fetch(APIURL + user);
console.log(resp)
const respData = await resp.json();
console.log(respData)
createUserCard(respData);
}
function createUserCard(user) {
const cardHTML = `
<div class="card">
<div>
<img src="${user.avatar_url}"
alt="${user.name}" />
</div>
<div>
<h2>${user.name}</h2>
<p>${user.bio}</p>
<ul>
<li>${user.followers}</li>
<li>${user.following}</li>
<li>${user.public_repos}</li>
</ul>
</div>
</div>
`;
main.innerHTML = cardHTML;
}
form.addEventListener('submit', (e) => {
e.preventDefault();
const user = search.value;
if (user) {
getUser(user);
search.value = "";
}
});
<html>
<head>
<title>Github Profile!</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
</head>
<body>
<form id="form">
<input type="text" id="search" placeholder="Search a User Here" />
</form>
<main id="main"></main>
<script src="script.js" defer></script>
</body>
</html>
Just add / after the users.
Your Code:
const APIURL = 'https://api.github.com/users';
async function getUser(user) {
const resp = await fetch(APIURL + user );
const respData = await resp.json();
createUserCard(respData);
}
Working Code:
const APIURL = 'https://api.github.com/users/';
async function getUser(user) {
const resp = await fetch(APIURL + user );
const respData = await resp.json();
createUserCard(respData);
}
This will get you the correct URL for the user
https://api.github.com/users/thesumitshrestha

How to display messages as list in gmail api?

I am creating a project where I can read messages of gmail. I am having problem to display list of messages. I've just quickstart sample code of getting labels.
https://developers.google.com/gmail/api/quickstart/js#step_1_set_up_the_sample
<!DOCTYPE html>
<html>
<head>
<title>Gmail API Quickstart</title>
<meta charset="utf-8" />
</head>
<body>
<p>Gmail API Quickstart</p>
<button id="authorize_button" onclick="handleAuthClick()">Authorize</button>
<button id="signout_button" onclick="handleSignoutClick()">Sign Out</button>
<pre id="content" style="white-space: pre-wrap;"></pre>
<script type="text/javascript">
const CLIENT_ID = 'XXXXX-XXXXXXXXXXXXX.apps.googleusercontent.com';
const API_KEY = 'XXXXXXXXXXXXXXXX';
const DISCOVERY_DOC = 'https://www.googleapis.com/discovery/v1/apis/gmail/v1/rest';
const SCOPES = 'https://www.googleapis.com/auth/gmail.readonly';
let tokenClient;
let gapiInited = false;
let gisInited = false;
document.getElementById('authorize_button').style.visibility = 'hidden';
document.getElementById('signout_button').style.visibility = 'hidden';
function gapiLoaded() {
gapi.load('client', intializeGapiClient);
}
async function intializeGapiClient() {
await gapi.client.init({
apiKey: API_KEY,
discoveryDocs: [DISCOVERY_DOC],
});
gapiInited = true;
maybeEnableButtons();
}
function gisLoaded() {
tokenClient = google.accounts.oauth2.initTokenClient({
client_id: CLIENT_ID,
scope: SCOPES,
callback: '', // defined later
});
gisInited = true;
maybeEnableButtons();
}
function maybeEnableButtons() {
if (gapiInited && gisInited) {
document.getElementById('authorize_button').style.visibility = 'visible';
}
}
function handleAuthClick() {
tokenClient.callback = async (resp) => {
if (resp.error !== undefined) {
throw (resp);
}
document.getElementById('signout_button').style.visibility = 'visible';
document.getElementById('authorize_button').innerText = 'Refresh';
await listLabels();
};
if (gapi.client.getToken() === null) {
tokenClient.requestAccessToken({prompt: 'consent'});
} else {
tokenClient.requestAccessToken({prompt: ''});
}
}
function handleSignoutClick() {
const token = gapi.client.getToken();
if (token !== null) {
google.accounts.oauth2.revoke(token.access_token);
gapi.client.setToken('');
document.getElementById('content').innerText = '';
document.getElementById('authorize_button').innerText = 'Authorize';
document.getElementById('signout_button').style.visibility = 'hidden';
}
}
async function listLabels() {
let response;
try {
response = await gapi.client.gmail.users.labels.list({
'userId': 'me',
});
} catch (err) {
document.getElementById('content').innerText = err.message;
return;
}
const labels = response.result.labels;
if (!labels || labels.length == 0) {
document.getElementById('content').innerText = 'No labels found.';
return;
}
const output = labels.reduce(
(str, label) => `${str}${label.name}\n`,
'Labels:\n');
document.getElementById('content').innerText = output;
}
</script>
<script async defer src="https://apis.google.com/js/api.js" onload="gapiLoaded()"></script>
<script async defer src="https://accounts.google.com/gsi/client" onload="gisLoaded()"></script>
</body>
</html>
This code is working fine without any errors.
I want to modify this code and get list of messages. But I am just unable to find right method to get list of messages.
Can someone please help me?
This is the documentation for labels. Compare that with the sample code you have for labels.
Then try to follow the same principle for messages. This is the documentation for messages. Following bullet 1, the code to get the list of messages might be something like
gapi.client.gmail.users.messages.list({'userId': 'me'});
You can work out how to parse the response from the linked documentation
Update:
The documentation says
each message resource contains only an id and a threadId. Additional message details can be fetched using the messages.get method.
This means you have to do something like
gapi.client.gmail.users.messages.get({
'userId': 'me',
'id' : <message_id>
});

Live Server works but I get 404 error for localhost

I have my server and client on different projects.
When I'm using Live Server on vsCode then everything works. But not if I try to go to localhost, then I get 404.
How come?
server.js
const logger = require('./logger');
const PORT = 3000;
const io = require('socket.io')(PORT);
const express = require('express');
const app = express();
const socketListeners = require('./sockets/socketListeners');
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
io.on('connection', socket => socketListeners(socket, io, logger));
index.js (on client)
const socket = io('http://localhost:3000');
window.addEventListener('DOMContentLoaded', appendForm());
With debugger on client I get "io is not defined".
When I use "import io from "socket.io-client" on line 1 I get unexpected identifier.
EDIT
index.html
<!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>Chat</title>
<link rel="stylesheet" href="style.css" />
<script src="http://localhost:3000/socket.io/socket.io.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.5/socket.io.min.js"></script>
<script src="name.js"></script>
<script src="index.js"></script>
<script src="chat.js"></script>
</head>
<body>
<section class="container"></section>
</body>
</html>
index.js
//const socket = io('http://localhost:3000');
const socket = io(':3000');
window.addEventListener('DOMContentLoaded', appendForm());
name.js
const container = document.querySelector('.container');
const nameContainer = document.createElement('section');
const title = document.createElement('h1');
const form = document.createElement('form');
const nameInput = document.createElement('input');
const submitName = document.createElement('button');
title.innerText = 'Enter Your Name';
submitName.innerText = 'Connect';
const getName = () => {
form.addEventListener('submit', e => {
e.preventDefault();
const name = nameInput.value;
socket.emit('new-user', name);
nameInput.value = '';
socket.off('invalid-name');
socket.on('invalid-name', () => {
feedbackBox(
"Make sure you don't have space or other funky characters in your name \n \n Must be between 3-13 characters"
);
});
socket.off('name-taken');
socket.on('name-taken', () => {
feedbackBox('Nickname already taken');
});
socket.off('user-accepted');
socket.on('user-accepted', () => {
title.classList.add('hide');
nameContainer.classList.add('hide');
addMessageForm();
});
});
};
const appendForm = () => {
nameInput.classList.add('name_input');
form.appendChild(nameInput);
submitName.classList.add('submit_name');
form.appendChild(submitName);
nameContainer.appendChild(form);
nameContainer.classList.add('name_container');
nameContainer.classList.remove('hide');
title.classList.remove('hide');
title.classList.add('name_title');
container.appendChild(title);
container.appendChild(nameContainer);
getName();
};
chat.js
//const socket = io('http://localhost:3000');
const showFeedback = document.createElement('section');
const messageContainer = document.createElement('section');
const messageForm = document.createElement('form');
const messageInput = document.createElement('input');
const submitMessage = document.createElement('button');
const disconnectButton = document.createElement('button');
submitMessage.innerText = 'Send';
disconnectButton.innerText = 'X';
messageContainer.classList.add('message-container');
messageForm.classList.add('send-container');
messageInput.classList.add('message-input');
submitMessage.classList.add('send-button');
disconnectButton.classList.add('disconnect-button');
const addMessageForm = () => {
showFeedback.classList.add('hide');
messageContainer.classList.remove('hide');
messageForm.classList.remove('hide');
disconnectButton.classList.remove('hide');
messageForm.appendChild(messageInput);
messageForm.appendChild(submitMessage);
container.appendChild(disconnectButton);
container.appendChild(messageContainer);
container.appendChild(messageForm);
appendMessage('You joined');
};
socket.on('chat-message', data => {
appendMessage(`${data.name}: ${data.message}`);
});
socket.on('user-connected', name => {
appendMessage(`${name} connected`);
});
socket.on('user-disconnected', name => {
appendMessage(`${name} left the chat`);
});
socket.on('user-inactivity-disconnected', name => {
appendMessage(`${name} was disconnected due to inactivity`);
});
messageForm.addEventListener('submit', e => {
e.preventDefault();
const message = messageInput.value;
socket.emit('send-chat-message', message);
if (message !== '') {
appendMyMessage(`You: ${message}`);
}
messageInput.value = '';
});
function appendMessage(message) {
const messageElement = document.createElement('section');
messageElement.innerText = message;
messageElement.classList.add('messages');
messageContainer.append(messageElement);
}
function appendMyMessage(message) {
const myMessageElement = document.createElement('section');
myMessageElement.innerText = message;
myMessageElement.classList.add('myMessage');
messageContainer.append(myMessageElement);
}
const feedbackBox = message => {
showFeedback.innerText = message;
showFeedback.classList.add('feedback-I-disconnect');
showFeedback.classList.remove('hide');
container.appendChild(showFeedback);
};
disconnectButton.addEventListener('click', event => {
if (event.target.classList.contains('disconnect-button')) {
socket.disconnect();
messageContainer.classList.add('hide');
messageForm.classList.add('hide');
disconnectButton.classList.add('hide');
appendForm();
feedbackBox('You disconnected from the chat');
socket.connect();
}
});
socket.on('inactive', () => {
socket.emit('disconnected');
messageContainer.classList.add('hide');
messageForm.classList.add('hide');
disconnectButton.classList.add('hide');
appendForm();
feedbackBox('Disconnected by the server due to inactivity');
});
First, fix your link to the Socket.io library in index.html. try cdn, like this <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.5/socket.io.min.js"></script>
I had a similar problem. Try this for connection to localhost:
const socket = io(':3000'); // pay attention on that it should be a string and starts with a colon symbol
EDIT
First you need to remove defer attribute from <script defer src="...> loading. This tells browser to keep loading the page while this defer script loading in background and run this script after it became loaded.
So, in your case, the script with Socket.io library will be run AFTER it required in the line const socket = io('...
Try using <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.5/socket.io.min.js"></script> in your client. I think it should work.

JavaScript how to change input value using public API and pure JavaScript

Could anyone explain to me why I cannot update my input value after clicking my submit button? My goal is to write a number, click the submit button and find the Pokémon with that number.
<!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>Document</title>
</head>
<body>
<div class="pokemon"></div>
<button id="btn" onclick="testFunc(inputValue)">SUBMIT</button>
<input type="text" value="" id="myInput">
<script>
const btn = document.getElementById("btn");
const input = document.getElementById("myInput");
let inputValue = input.value;
const testFunc = function(a) {
const apiData = {
url: 'https://pokeapi.co/api/v2/',
type: 'pokemon',
id: a,
}
const { url, type, id } = apiData
const apiUrl = `${url}${type}/${id}`
fetch(apiUrl)
.then((data) => {
if (data.ok) {
return data.json()
}
throw new Error('Response not ok.');
})
.then(pokemon => generateHtml(pokemon))
.catch(error => console.error('Error:', error))
const generateHtml = (data) => {
console.log(data)
const html = `
<div class="name">${data.name}</div>
<img src=${data.sprites.front_default}>
<div class="details">
<span>Height: ${data.height}</span>
<span>Weight: ${data.weight}</span>
</div>
`
const pokemonDiv = document.querySelector('.pokemon')
pokemonDiv.innerHTML = html
}
}
</script>
</body>
</html>
I will be grateful for any advice.
Best regards
You need to move the inputValue retrieval inside the testFunc function.
const testFunc = function() {
let inputValue = input.value;
const apiData = {
url: 'https://pokeapi.co/api/v2/',
type: 'pokemon',
id: inputValue,
}
The button's onclick only knows about itself, it cannot reference input.
const btn = document.getElementById("btn");
const input = document.getElementById("myInput");
const testFunc = function() {
let inputValue = input.value;
const apiData = {
url: 'https://pokeapi.co/api/v2/',
type: 'pokemon',
id: inputValue,
}
const { url, type, id } = apiData
const apiUrl = `${url}${type}/${id}`
fetch(apiUrl)
.then((data) => {
if (data.ok) {
return data.json()
}
throw new Error('Response not ok.');
})
.then(pokemon => generateHtml(pokemon))
.catch(error => console.error('Error:', error))
const generateHtml = (data) => {
//console.log(data) <-- Slows down the result
const html = `
<div class="name">${data.name}</div>
<img src=${data.sprites.front_default}>
<div class="details">
<span>Height: ${data.height}</span>
<span>Weight: ${data.weight}</span>
</div>
`
const pokemonDiv = document.querySelector('.pokemon')
pokemonDiv.innerHTML = html
}
}
<div class="pokemon"></div>
<button id="btn" onclick="testFunc()">SUBMIT</button>
<input type="text" value="25" id="myInput"> <!-- Default to Pikachu -->

Categories

Resources