FormData object is empty when using vue #submit directive - javascript

I came across some weird edge case and I narrowed it down to a solution. I created a form with vue and empty strings were being sent down to the server. I narrowed it down to the #submit directive. If I write form.addEventListener('submit') everything works as it should but with the #submit directive it does not work.
The solution is to use the addEventListener method but I'm not sure if I did something wrong here or if this is a bug with vue's v-on directive
Runnable example: https://stackblitz.com/edit/web-platform-izreva?file=index.html
NOTE: I know about the form data object and how to view it with
for (const [key, value] of formData.entries()) {
console.log(`${key}: ${value})
}
In the below snippet if you uncomment the code then it works with addEventListener.
const form = document.querySelector('#test-form')
const app = Vue.createApp({
name: 'test app',
methods: {
test(e) {
e.preventDefault()
console.log(e)
const formData = new FormData(form)
for (const [key, value] of formData.entries()) {
console.log(`${key}: ${value}`)
}
}
}
}).mount('#app-main')
// const form = document.querySelector('#test-form')
// form.addEventListener('submit', e => {
// e.preventDefault()
// const formData = new FormData(form)
// for (const [key, value] of formData.entries()) {
// console.log(`${key}: ${value}`)
// }
// })
<!DOCTYPE html>
<html lang="en">
<head>
<title>Home</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<script src="https://unpkg.com/vue#3.2.47/dist/vue.global.js" defer></script>
</head>
<body>
<h1>app</h1>
<div id="app-main">
<form #submit="test" id="test-form">
<input type="text" name="foo">
<button type="submit">submit</button>
</form>
</div>
<script src="./index.js" defer></script>
</body>
</html>

I imagine this is an issue with how the CDN version of Vue parses a template from existing HTML.
Each element is replaced with the Vue compiled element equivalent.
The form you captured at the start is no longer the one emitting the submit event. You can easily check this by comparing form === e.target.
If you use the following it works as expected.
const formData = new FormData(e.target);
In short, you're doing things in a very non-Vue way so it's little surprise you've encountered some friction.

Related

Need id in url from js

I need a script that fetch id from javascript into an url
For example: https://google.com/youtube[id].mp3
javascript currently:
$(document).ready(function() {
// FETCHING DATA FROM JSON FILE
$.getJSON("https://api.omny.fm/orgs/56ccbbb7-0ff7-4482-9d99-a88800f49f6c/programs/a49c87f6-d567-4189-8692-a8e2009eaf86/clips/",function(data) {
$('#table').html(data.Clips[0].Id);
});
});
html currently:
<html lang="en"><head>
<meta charset="UTF-8">
<link rel="stylesheet" href="./style.css">
</head>
<meta http-equiv="refresh" content="0; url=https://traffic.omny.fm/d/clips/56ccbbb7-0ff7-4482-9d99-a88800f49f6c/a49c87f6-d567-4189-8692-a8e2009eaf86/<p id="table">/audio.mp3" />
<script src="https://code.jquery.com/jquery-3.5.1.js"></script>
<script src="./script.js"></script>
</html>
i hope someone can help me out to get this working :)
thx.
tried multiple things to get id into url but not working
Your question is very unclear, but I assume what you're trying to do is fetch the JSON data, then append it to a table.
The first reason things aren't working is likely because your HTML is invalid. Specifically right here:
<!-- Why are you trying to embed a p tag into a link? -->
<meta http-equiv="refresh" content="0; url=https://traffic.omny.fm/d/clips/56ccbbb7-0ff7-4482-9d99-a88800f49f6c/a49c87f6-d567-4189-8692-a8e2009eaf86/<p id="table">/audio.mp3" />
The second issue is that you're using the jQuery .html() method, which actually just sets the innerHTML of an element. This is dangerous, and you should not do this (especially with fetched data).
// This is not good for many reasons
$('#table').html(data.Clips[0].Id);
Also, why are you using jQuery in 2023? DOM APIs are modern and extensive nowadays. jQuery is nothing but an abstraction on top of them. Here's a solution I believe will help you.
const URI = 'https://api.omny.fm/orgs/56ccbbb7-0ff7-4482-9d99-a88800f49f6c/programs/a49c87f6-d567-4189-8692-a8e2009eaf86/clips/';
const tableBody = document.querySelector('table > tbody');
const createCell = (content) => {
const cell = document.createElement('td');
cell.textContent = content;
return cell;
};
const createRow = (id, title) => {
const row = document.createElement('tr');
row.appendChild(createCell(id));
row.appendChild(createCell(title));
return row;
};
document.addEventListener('DOMContentLoaded', async () => {
// Use the native "fetch" API to fetch the data.
const res = await fetch(URI);
// Convert the data to JSON using the ".json()" method.
const json = await res.json();
// Loop through all of the clips in the received array.
for (const clip of json.Clips) {
// For each clip, add a new row to the table.
tableBody.appendChild(createRow(clip.Id, clip.Title));
}
});
<table>
<thead>
<tr>
<th>ID</th>
<th>Title</th>
</tr>
</thead>
<tbody></tbody>
</table>

How to get REST API JavaScript fetch object value to HTML?

How can I get printed console object value to HTML?
I have JavaScript fetch code like this:
const comments = fetch("https://api.github.com/repos/pieceofdiy/comments/issues/1")
.then((response) => response.json())
.then((labels) => {
return labels.comments;
});
const printComments = () => {
comments.then((number) => {
console.log(number);
});
};
printComments()
printComments() numeric object value shows correct in console, but how to show it in HTML
to <span id="comments">..</span> ?
With JS you can edit the DOM Hierarchy by searching for your desired Element to change.
const commentsEl = document.querySelector('.comments');
commentsEl.innerHTML = printComments();
With document.querySelector(CSS-Selector) you can search the DOM-Tree for a sufficient Element matching your Selector
We store the Element in a variable and change the Content of this Element by saving the comments in the property .innerHTML.
I've added a snippet demonstrating the changes below, and also changed some bits to improve your code.
As the fetch-Method is asynchronous, you’ll see fetching comments ... for a brief moment, as we change the content when the fetch finished and we got the results.
const commentsEl = document.querySelector('.comments');
// We fetch the comments as before
fetch("https://api.github.com/repos/pieceofdiy/comments/issues/1")
.then((response) => response.json())
.then((labels) => {
// But when we get the results, we immedietly change the contents of the comments span.
commentsEl.innerHTML = labels.comments;
});
<div class="container">
<p>Comments:</p>
<span class="comments">Fetching comments ...</span>
</div>
You could try setting a p tag with an id, ex: <p id=“comments”>and then using document.getElementById(“comments”).innerValue = number;
Place that second piece of code into printComments()
First you need to get your span tag in your html document.
Then define the innerHtml property of the span element by the value returned by the promise, in this case in your case the value is returned through a callback, so you simply have to perform the process in the scope of the callback.
Here is a snippet to illustrate this:
<!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>
<span id="comments"></span>
<script>
const span = document.getElementById("comments");
const comments = fetch("https://api.github.com/repos/pieceofdiy/comments/issues/1")
.then((response) => response.json())
.then((labels) => {
return labels.comments;
});
comments
.then(res => span.innerHTML = res)
.catch(err => console.log(err));
</script>
</body>
</html>
But it can be done more cleanly this way:
<!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>
<ol>
<li>Comments: <span id="comments1"></span></li>
<li>Comments: <span id="comments2"></span></li>
<li>Comments: <span id="comments3"></span></li>
</ol>
<script>
const comments1 = document.getElementById("comments1");
const comments2 = document.getElementById("comments2");
const comments3 = document.getElementById("comments3");
const printComment = async (url, HTMLTag) => {
try {
const request = await fetch(url);
const response = await request.json();
HTMLTag.innerHTML = response.comments;
} catch (error) {
console.log(error);
}
};
printComment("https://api.github.com/repos/pieceofdiy/comments/issues/1", comments1);
printComment("https://api.github.com/repos/pieceofdiy/comments/issues/1", comments2);
printComment("https://api.github.com/repos/pieceofdiy/comments/issues/1", comments3);
</script>
</body>
</html>
Good luck !

Add multiple values to localStorage and then retrieve a random one in JavaScript

I am trying to make a system where the user inputs multiple values at separate times and then can click a button that tells them one of their inputs (at random). This is what I have so far:
(Note: for some odd reason this code snippet is not functioning properly here (at least not on my side) but it is working just fine in JS Bin and even my own Notepad++)
function store(){
var toStore = document.getElementById("itemInputBox");
localStorage.setItem("itemInputBox", toStore.value);
}
function get(){
var toGet = localStorage.getItem("itemInputBox");
document.getElementById("outputArea").innerHTML = toGet;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<form id="itemForm">
<input type="text" id="itemInputBox" autocomplete="off" onmouseout="store()"><br><br>
<button id="getStoredValue" onclick="get()" type="button" style="cursor:pointer;">Get Input Value</button>
</form>
<div id="outputArea"></div>
</body>
</html>
As I am fairly new to coding (have been doing it for only a year or so) and I am also new to localStorage, I need some guidance for this. Thanks to whoever responds!
You can use Arrays in Javascript to store multiple values in the localStorage. When storing the values, they should be in JSON format. Also, when retrieving the values you have to pass that JSON string to `JSON.parse(array) and then retrieve the values.
To get a random value from that array, you can use Math functions in JavaScript. Please check the example below. It won't work in the StackOverflow code snippets section because it restricts the LocalStorage functions but you can check it locally.
function store(){
var toStore = document.getElementById("itemInputBox");
var valuesJSON = get();
if (valuesJSON == null){
createLocalStorageKey();
}
var values = JSON.parse(get());
values.items.push(toStore.value);
localStorage.setItem("itemInputBox", JSON.stringify(values));
}
function createLocalStorageKey(){
localStorage.setItem("itemInputBox", JSON.stringify({items:[]}));
}
function get(){
var toGet = localStorage.getItem("itemInputBox");
return toGet;
}
function parseValueToHTML(){
var valuesJSON = get();
if (valuesJSON == null){
createLocalStorageKey();
}
var values = JSON.parse(get());
document.getElementById("outputArea").innerHTML = values.items[Math.floor(Math.random() * values.items.length)];
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<form id="itemForm">
<input type="text" id="itemInputBox" autocomplete="off"><br><br>
<button onclick="store()" type="button">Store Value</button>
<button id="getStoredValue" onclick="parseValueToHTML()" type="button" style="cursor:pointer;">Get Input Value</button>
</form>
<div id="outputArea"></div>
</body>
</html>
You can use an array to store your values
Before you add a new value you'll have to retrieve the array from local storage
Add the new value to the array and replace the previous array in local storage with the updated one
Use JSON to serialize the data to store it in local storage
If there is no data in local storage create an array with the value
For the get functionality, after you retrieve the array you can generate a random number from 0 to the length of the array-1 to choose a value by index.
my way...
const itemForm = document.forms['item-form']
, getAllItems = _ =>
{
let r = localStorage.getItem('itemInputBox')
return !r ? [] : JSON.parse(r)
}
, setAllItems = arr =>
{
localStorage.setItem('itemInputBox',JSON.stringify(arr))
}
;
itemForm.setValueStored.onclick=()=>
{
let val = itemForm.itemInputBox.value.trim()
if (val)
{
let store = getAllItems()
if (!store.include(val))
{
store.push(val)
setAllItems(store)
}
}
itemForm.itemInputBox.value = ''
}
itemForm.getStoredValue.onclick=()=>
{
let store = getAllItems()
itemForm.itemInputBox.value = (!store.length) ? '' : store[Math.floor(Math.random() *store.length)]
}
<form name="item-form">
<input type="text" name="itemInputBox" autocomplete="off">
<br><br>
<button name="setValueStored" type="button">Set value in store</button>
<button name="getStoredValue" type="button">Get random input value from store</button>
</form>

addEventListener in shadow DOM

I am trying to get user's input from Shadow DOM with addEventListener, but I don't get any response, any error. References to the elements work correctly, so console.log(input) prints out the correct element. The same code works perfectly fine without shadow DOM but I have to include it in my project. This is my code (these events are just for checking of course):
const template = document.createElement('template')
template.innerHTML = /* html */`
<div id="username">
<p>Please choose your username!</p>
<input id="username_input" type="text" placeholder="enter username">
<button>Ok</button>
</div>
`
/**
* #class Quiz
* #extends {window.HTMLElement}
*/
export class Quiz extends window.HTMLElement {
constructor () {
super()
this.attachShadow({ mode: 'open' })
this.shadowRoot.appendChild(template.content.cloneNode(true))
}
getUserName () {
const button = this.shadowRoot.querySelector('button')
const inputUname = this.shadowRoot.querySelector('#username input')
console.log(button)
button.addEventListener('click', event => console.log('badum tss'))
inputUname.addEventListener('input', event => console.log('badums tss'))
}
}
window.customElements.define('user-name', Quiz)
app.js code:
import { Quiz } from './quiz.js'
var x = new Quiz()
x.getUserName()
html:
<!doctype html>
<html lang="">
<head>
<meta charset="utf-8">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script type="module" src="js/app.js"></script>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<user-name></user-name>
<script nomodule src="build.js"></script>
</body>
</html>
The problem is quite simple. Your code actually creates two <user-name>s.
One is statically created in html:
<user-name></user-name>
The other is dynamically created by javascript:
var x = new Quiz()
The dynamic one is never attached to the document, so you can only see the static one on the page. Since you only call getUserName() on the dynamic one, the click and input listeners are never added to the static one. That's why you didn't get any response.
I would suggest that you put addEventListener() in the constructor, then the static one should function normally.

Why did fetch() API failed to retrieve custom HIBP JSON data?

I am trying to use the Have I Been Pwned? API to retrieve a list of breaches for a given email account.
I retrieve this list using the fetch() API. In the browser it looks like there is a connection to the HIBP website but the expected breaches are not visible.
I think this is a JSON problem because the API returns results without a root tree (?) (e.g. [breaches:{"Name"... - only the {"Name"}), so I think I'm making a mistake at the iteration step in the JS file. Also, I'm not calling the 'retrieve' function in the HTML file correctly because the browser throws an error: 'Uncaught ReferenceError: retrieve is not defined', but this is a side-issue (fetch('https://haveibeenpwned.com/api/v2/breachedaccount/test#example.com') doesn't work either).
This is my first week working with JS, fetch(), and JSON, so I consulted a couple of sources before asking this question (but I still can't figure it out, after a couple of days):
How to Use the JavaScript Fetch API to Get Data
fetch API
API methods for HaveIBeenPwnd.com (unofficial)
Where is the actual problem?
The index.html file:
<!DOCTYPE html>
<html lang=en>
<head>
<title>test</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, nofollow">
</head>
<body id="top">
<header id="header">
<div class="content">
<h1 style="text-align: center">Put an email in this box</h1>
<input type="email" id="InputBox" value="" autocapitalize="off" spellcheck="false" />
<button type="submit" id="PwnedButton" onclick="retrieve">pwned?</button>
<ul id="results"></ul>
</div>
</header>
<script src="test.js"></script>
</body>
</html>
The test.js file (I know that JS is an interpreted language - so empty characters affect execution speed - but I made it more readable for this example):
function createNode(element) {
return document.createElement(element); // Create the type of element you pass in the parameters
}
function append(parent, el) {
return parent.appendChild(el); // Append the second parameter(element) to the first one
}
const account = document.getElementById('InputBox');
const PwnedButton = document.getElementById('PwnedButton');
const results = document.getElementById('results');
fetch('https://haveibeenpwned.com/api/v2/breachedaccount/' + account)
.then((resp) => resp.json()) // Transform the data into json
.then(function(retrieve) {
let breaches = retrieve.Name; // Get the results
return breaches.map(function(check) { // Map through the results and for each one run the code below
let span = createNode('span'); // Create the element we need (breach title)
span.innerHTML = `${breaches}`;
append(results, span);
})
})
.catch(function(error) {
console.log(JSON.stringify(error));
});
let breaches = retrieve.Name;
retrieve is not an object with a Name property.
It is an array containing multiple objects, each of which has a Name property.
You have to loop over it.
e.g.
retrieve.forEach( item => {
let breaches = retrieve.Name;
console.log(breaches);
});
breaches.map
… and the Name is a string, so you can't map it. You can only map an array (like the one you have in retrieve).
I have created working version of what are you possible going to implement, taking Name field from result. https://jsfiddle.net/vhnzm1fu/1/ Please notice:
return retrieve.forEach(function(check) {
let span = createNode('span');
span.innerHTML = `${check.Name}<br/>`;
append(results, span);
})

Categories

Resources