How to append values to a form in sveltekit? - javascript

So, I am using the default behavior in sveltekit, in which we use html forms to submit data to backend, but there is also a file upload that uses ajax (fetch api) to upload image to server and get its file name, now I want to append these names to the html form state in somehow, is this possible or should I move to full state managed forms instead?
here is my code:
<script lang="ts">
import { enhance } from '$app/forms';
import type { ActionData } from './$types';
let input: HTMLInputElement;
let container;
let images: string[] = [];
let placeholder;
let showImage = false;
let files: Blob[] = [];
let form: ActionData;
const uploadImage = async (file: Blob) => {
let formdata = new FormData();
formdata.append('image', file);
const res = await fetch('/api/upload', {
method: 'POST',
body: formdata
});
const resp = await res.json();
console.log({ resp });
};
const onChange = async () => {
const imgFiles = input.files;
if (imgFiles) {
for (let i = 0; i < imgFiles.length; i++) {
const reader = new FileReader();
reader.addEventListener('load', () => {
if (typeof reader.result == 'string') {
images = [...images, reader.result];
// upload the reader result to backend
}
});
reader.readAsDataURL(imgFiles[i]);
files = [...files, imgFiles[i]];
await uploadImage(imgFiles[i]);
}
}
};
</script>
<form
use:enhance
class="flex rounded-lg shadow-lg my-10 sm:w-1/2 md:w-2/4 flex-col mx-auto p-2 sm:p-6"
method="post"
action="/products/add"
>
{#if form && form.success}
<p class="">Product Added Sucessfully</p>
{/if}
<h2 class="text-2xl font-bold">Add a product / service</h2>
<div class="my-4">
<label for="prodSer">Is this a product or a service?</label>
<select name="type">
<option value="product">Product</option>
<option value="service">Service</option>
</select>
</div>
<input class="basicInput" name="name" placeholder="Name of your product" />
<textarea
class="textArea"
rows={5}
name="description"
placeholder="Write a description about it"
/>
<div class="h-full">
<h4>Images</h4>
<label for="imagePicker">Choose images</label>
<input
multiple
bind:this={input}
on:change={onChange}
id="imagePicker"
name="productImages"
type="file"
placeholder="Upload media"
/>
<div class="flex h-full flex-row my-4 mx-2">
{#if images.length > 0}
{#each images as image}
<div class="h-64 mx-2">
<img class="h-64" src={image} />
<button class="">Remove</button>
</div>
{/each}
{/if}
</div>
</div>
<div>
<label for="currency">Currency</label>
<select id="currency" name="currency">
<option value="Rs.">Rupees</option>
</select>
</div>
<div class="flex flex-row justify-between">
<input class="basicInput halfInput" min={0} name="mrp" placeholder="MRP" type="number" />
<input class="basicInput halfInput" min={0} name="price" placeholder="Price" type="price" />
</div>
<div class="flex flex-row justify-between">
<input class="basicInput halfInput" min={0} name="gst" placeholder="GST %" type="number" />
<input class="basicInput halfInput" min={0} name="cess" placeholder="Cess %" type="price" />
</div>
<div class="p-2">
<label class="text-sm text-gray-600" for="taxIncl">Price is Inclusive of tax?</label>
<input id="taxIncl" type="checkbox" name="includeTax" />
</div>
<div>
<input class="basicInput" name="unit" placeholder="Unit" />
</div>
<div>
<input class="basicInput" name="hsn" placeholder="HSN/SAC" />
</div>
<div>
<input class="basicInput" name="stock" placeholder="Opening Stock" type="number" />
</div>
<input
class="my-2 sm:my-4 sm:w-1/2 hover:cursor-pointer text-white bg-blue-700 hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300 font-medium rounded-full text-sm px-5 py-2.5 text-center mr-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
type="submit"
value="Add"
/>
</form>
<style>
.basicInput {
height: 60px;
width: 100%;
border-radius: 5px;
border: 1px solid #3e3e3e;
margin-top: 10px;
padding: 10px;
}
.textArea {
width: 100%;
border-radius: 5px;
border: 1px solid #3e3e3e;
margin-top: 10px;
padding: 10px;
}
.halfInput {
width: 49%;
}
select {
padding: 8px;
border-radius: 4px;
}
</style>

I recently came across this issue and might have a solution.
I use a component called Form which looks like this (using vanilla JS with JSDoc notation):
<script>
import { createEventDispatcher } from 'svelte';
import { enhance } from '$app/forms';
const dispatch = createEventDispatcher();
/** #type {string} */
export let name;
/** #type {string} */
export let method = 'POST';
/** #type {string | null} */
export let action = null;
/** #type {Object} */
export let attributes = {};
/** #type {string | null} */
export let _class = null;
/** #type {string | null} */
export let autocomplete = 'off';
/** #type {boolean} */
export let do_update = true;
/** #type {function | null} */
export let manipulation = null;
/** #param {any} event */
function handleResult(event) {
dispatch('post_result', event);
}
</script>
<form
{name}
{method}
{action}
id={name}
class={_class}
{autocomplete}
{...attributes}
use:enhance={({ data }) => {
if (manipulation) manipulation(data);
return async ({ result, update }) => {
if (do_update) update();
handleResult(result);
};
}}
>
<slot />
</form>
You'll note the variable manipulation which is used during the use:enhance callback. It references a function which directly accesses and manipulates the form's data. I suppose this works thanks to the way JavaScript references/mutates objects. In a component or page, I can manipulate the form data like so:
<script>
/** #param {FormData} data */
function manipulateForm(data) {
value = 'myvalue' // Provide your values here
data.set('set_field', value);
data.delete('delete_field');
// ...and so on
}
</script>
<Form {name} manipulation={manipulateForm}>
When request.formData() is called, it returns the manipulated form data.

Related

Handling Form in Nextjs

In this code example I would like to try to do easy math (ket=name + names). The sum should be a number and displayed as “ket”. But when I type in 3 and 6 in my input fields, “ket” shows up to be 36 (and not 9).
export default function Kettenl() {
const submitContact = async (event) => {
event.preventDefault();
const names = event.target.names.value;
const name = event.target.name.value;
let ket = name + names;
alert(`So your name is ${ket}?`);
};
return (
<div className="max-w-xs my-2 overflow-hidden rounded shadow-lg">
<div className="px-6 py-4">
<div className="mb-2 text-xl font-bold">Contact us</div>
<form className="flex flex-col" onSubmit={submitContact}>
<label htmlFor="name" className="mb-2 italic">
Name
</label>
<input
className="mb-4 border-b-2"
id="name"
name="name"
type="number"
required
/>
<label htmlFor="name" className="mb-2 italic">
Names
</label>
<input
className="mb-4 border-b-2"
id="names"
name="names"
type="number"
required
/>
<button
type="submit"
className="px-4 py-2 font-bold text-white bg-blue-500 rounded-full hover:bg-blue-700"
>
Submit
</button>
</form>
</div>
</div>
);
}
That is because the values are being read as strings
'3'+'6'='36'
you should change it to this
const names = parseInt(event.target.names.value);
const name = parseInt(event.target.name.value);
Also, you are storing and accessing the values incorrectly. You should learn the basics of react before moving on to next.js.
https://reactjs.org/docs/forms.html
Read about controlled and uncontrolled inputs from the docs
So even if your app works with the aforementioned change, you shouldn't be doing it like this.
Thats because values are returned as a string by default from your target element. For inputs with type="number" you can just use the input value with e.target.names.valueAsNumber.
const submitContact = async (event) => {
event.preventDefault();
const names = event.target.names.valueAsNumber;
const name = event.target.name.valueAsNumber;
let ket = name + names;
alert(`So your name is ${ket}?`);
};

Modal pop up on submit in React

I am trying to display a modal pop up on submit in a form in React. Right now, the invalid email and name modal pops up just fine. The problem is, It will not display the alternate success message if the form was submitted correctly. I also am not sure if it will persist upon successful submission, but can't get past this problem to test it out. Please let me know where I am going wrong. Thanks!!
Contact.js:
import React, { useState } from "react";
import { FaEnvelope } from "react-icons/fa";
import Modal from "./Modal";
const Contact = (props) => {
const [showModal, setShowModal] = useState();
const [enteredName, setEnteredName] = useState("");
const [enteredEmail, setEnteredEmail] = useState("");
const contactHandler = (event) => {
event.preventDefault();
if (enteredName.trim().length === 0 || enteredEmail.trim().length === 0) {
setShowModal({
title: "Invalid Input",
message: "Please enter a valid name and email",
});
return;
}
if (enteredName.trim().length > 0 || enteredEmail.trim().length > 0) {
setShowModal({
title: "Thank You",
message: "Your form as been submitted",
});
return;
}
props.Contact(enteredName, enteredEmail);
setEnteredName("");
setEnteredEmail("");
};
const modalHandler = () => {
setShowModal(null);
};
return (
<div>
{showModal && (
<Modal
title={showModal.title}
message={showModal.message}
onShowModal={modalHandler}
/>
)}
<div className="w-full h-screen main flex justify-center items-center p-4">
<form
onSubmit={contactHandler}
method="POST"
action="https://getform.io/f/8e30048c-6662-40d9-bd8b-da62595ce998"
className="flex flex-col max-w-[600px] w-full"
>
<div className="pb-8">
<p className="text-4xl font-bold inline border-b-4 bdr text-main">
Contact
</p>
<span>
<FaEnvelope className="inline-flex ml-4 text-4xl text-main" />
</span>
<p className="text-main py-2">
Please enter your info below and I will be back with you within 24
hours. You can also email me directly at:
<a
href="mailto:chris.t.williams417#gmail.com"
className="ml-2 font-bold hover:text-[#FFE5b4]"
>
chris.t.williams417#gmail.com
</a>
</p>
</div>
<input
className="form-bg p-2"
type="text"
placeholder="Name"
name="name"
/>
<input
className="my-4 py-2 form-bg"
type="email"
placeholder="Email"
name="email"
/>
<textarea
className="form-bg p-2"
name="message"
rows="10"
placeholder="Message"
></textarea>
<button className="con-btn">Submit</button>
</form>
</div>
</div>
);
};
export default Contact;
Modal.js:
import React from "react";
const Modal = (props) => {
return (
<div>
<div className="backdrop" onClick={props.onShowModal} />
<div className="modalPos">
<div className="header">
<h2>{props.title}</h2>
</div>
<div className="content">
<p>{props.message}</p>
</div>
<div className="actions">
<button onClick={props.onShowModal} className="hero-btn">
Exit
</button>
</div>
</div>
</div>
);
}
export default Modal
*disclaimer : my grammar so bad so i am apologize about that.
i just try your code and when i submit is always invalid, i check it and the state name and email is empty string when i submit because your forgot implement onChangeHanlde in input.
create function to handle onChange
const onChangeHandler = (field, event) => {
if (field == "email") {
setEnteredEmail(event.target.value);
} else if (field == "name") {
setEnteredName(event.target.value);
}
};
and add onChange, value attribute in input tag both name and email
<input
className="form-bg p-2"
type="text"
placeholder="Name"
name="name"
onChange={(e) => {
onChangeHandler("name", e);
}}
value={enteredName}
/>
<input
className="my-4 py-2 form-bg"
type="email"
placeholder="Email"
name="email"
onChange={(e) => {
onChangeHandler("email", e);
}}
value={enteredEmail}
/>
You didn't put
value = {enteredName}
in your input element. so your states will not be set by user's input.

Infinite loop inside React JS

So, I found similar issues that were not quite the same and didn't help me understand my problem.
I'm building a simple task manager using React and Firebase. I'm fetching data using a custom hook, since different components need to fetch data from Firebase. When the page loads, I get an infinite loop inside the custom hook, that's my issue.
Here's the component that triggers the custom hook:
const AllTasks = () => {
const [tasks, setTasks] = useState([]);
const renderTasks = (taskObj) => {
const loadedTasks = [];
for (const key in taskObj) {
loadedTasks.unshift({
id: key,
date: taskObj[key].Date,
client: taskObj[key].Client,
task: taskObj[key].Task,
time: taskObj[key].Time,
});
}
setTasks(loadedTasks);
};
const { sendRequest: retrieveTasks } = useHttp();
const refresh = retrieveTasks(
{
url: "https://myurl.com/alltasks.json",
},
renderTasks
);
// Loads tasks
// useEffect(() => {
// retrieveTasks(
// {
// url: "https://myurl.com/alltasks.json",
// },
// renderTasks
// );
// }, []);
// Takes input value from task form and sends it to Firebase
const enteredDateRef = useRef();
const enteredClientRef = useRef();
const enteredTaskRef = useRef();
const enteredTimeRef = useRef();
const renderNewTask = (data) => {
const taskData = {
id: data.name,
date: data.Date,
client: data.Client,
task: data.Task,
time: data.Time,
};
setTasks((tasks) => [taskData, ...tasks]);
};
const { sendRequest: postTask } = useHttp();
const submitTaskHandler = async (event) => {
event.preventDefault();
const enteredDate = enteredDateRef.current.value;
const enteredClient = enteredClientRef.current.value;
const enteredTask = enteredTaskRef.current.value;
const enteredTime = enteredTimeRef.current.value;
const newTaskData = {
Date: enteredDate,
Client: enteredClient,
Task: enteredTask,
Time: enteredTime,
};
postTask(
{
url: "https://myurl.com/alltasks.json",
method: "POST",
body: newTaskData,
headers: { "Content-Type": "application.json" },
},
renderNewTask
);
};
return (
<Fragment>
<TasksHeader />
<section className="w-full mt-4">
<CardDark backgroundType="liquid">
<h2>This month alone</h2>
<div className="flex mt-4 justify-between">
<div>
<p className=" text-6xl font-medium">56.4</p>
<p className="mt-2">Hours input</p>
</div>
<div className="border border-white"></div>
<div>
<p className=" text-6xl font-medium">1,378.45 €</p>
<p className="mt-2">Cash generated</p>
</div>
<div className="border border-white"></div>
<div>
<p className=" text-6xl font-medium">128</p>
<p className="mt-2">Tasks input</p>
</div>
</div>
</CardDark>
</section>
<section>
<SearchBar />
<div className="flex justify-between mt-4">
<h2>Your tasks</h2>
<div className="flex">
<h2 className="mr-2">Filters:</h2>
<form className="text-gray-400" action="">
<input type="date" className=" bg-gray-50 w-[120px] mr-2" />
<input type="date" className=" bg-gray-50 w-[120px] mr-2" />
<select id="cars" name="cars" className="bg-gray-50">
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="fiat">Fiat</option>
<option value="audi">Audi</option>
</select>
</form>
</div>
</div>
</section>
<section>
<CardWhite>
<form
action=""
onSubmit={submitTaskHandler}
className="h-[50px] flex"
>
<input
ref={enteredDateRef}
type="date"
className="h-[50px] focus:outline-none w-1/5 rounded-l-2xl border border-gray-300 px-4"
/>
<input
ref={enteredClientRef}
type="text"
className="h-[50px] focus:outline-none w-1/5 border-y border-gray-300 px-4"
placeholder="Client name"
/>
<input
ref={enteredTaskRef}
type="text"
className="h-[50px] focus:outline-none grow border-y border-l border-gray-300 px-4"
placeholder="Task"
/>
<div className="h-[50px] flex w-1/10 rounded-r-2xl border border-gray-300 pl-4">
<input
ref={enteredTimeRef}
type="time"
className="focus:outline-none h-full"
/>
<button type="submit" className="h-full p-2">
<img src={SubmitIcon} className="h-full" alt="" />
</button>
</div>
</form>
<List
tasks={tasks}
refresh={refresh}
/>
</CardWhite>
</section>
</Fragment>
);
};
export default AllTasks;
and here's the custom hook:
const useHttp = () => {
const sendRequest = async (requestConfig, applyData) => {
const response = await fetch(requestConfig.url, {
method: requestConfig.method ? requestConfig.method : "GET",
body: requestConfig.body ? JSON.stringify(requestConfig.body) : null,
headers: requestConfig.headers ? requestConfig.headers : {},
});
const responseData = await response.json();
applyData(responseData);
console.log('request');
};
return { sendRequest };
};
export default useHttp;
I'm guessing it has something to do with the way the custom hook is called inside the component, but I'm completely lost. I don't even know what triggers it in the first place. Can anyone help me? My goal is to trigger it once, when the page loads and when I submit a new task with the form.
I put the whole code since I don't really where the problems are.
Thanks in advance.

How Can i send image and text user information in react js

Sign up Form Written in JSX
import './SignUp-Form-CSS.css'
import { Link } from 'react-router-dom';
import { useState, useContext } from 'react';
import AuthContext from '../Context_store/AuthContext/AuthContext';
const SignUp = () => {
const [name, setName] = useState(null);
const [password, setPassword] = useState(null);
const [confirmPassword, setConfirmPassword] = useState(null);
const [image, setImage] = useState(null);
const authCtx = useContext(AuthContext);
const nameChangeHandeler = (event) => {
setName(event.target.value)
}
const passwordChangeHandeler = (event) => {
setPassword(event.target.value)
}
const confirmPasswordChangeHandeler = (event) => {
setConfirmPassword(event.target.value);
}
const imageChangeHandeler = (event) => {
setImage(event.target.files[0]);
console.log(event.target.files[0]);
}
const onSubmitHandeler = (event) => {
event.preventDefault()
// const data = {
// username: name,
// password: password,
// confirmPassword: confirmPassword,
// image: image
// }
const data=new FormData();
data.append("name",name);
data.append("password",password);
data.append("confirmPassword",confirmPassword);
data.append("image",image);
// data.append('username',name);
console.log(data);
authCtx.signup(data)
}
return (
<div class="container">
<div class="row">
<div class="col-lg-10 col-xl-9 mx-auto">
<div class="card flex-row my-5 border-0 shadow rounded-3 overflow-hidden">
<div class="card-img-left d-none d-md-flex">
{/* <!-- Background image for card set in CSS! --> */}
</div>
<div class="card-body p-4 p-sm-5">
<h5 class="card-title text-center mb-5 fw-light fs-5">Register</h5>
<form onSubmit={onSubmitHandeler} encType='multipart/form-data' >
<div class="form-floating mb-3">
<input type="text" class="form-control"
id="floatingInputUsername"
onChange={nameChangeHandeler}
placeholder="myusername" required autofocus />
<label for="floatingInputUsername">Username</label>
</div>
{/* <div class="form-floating mb-3">
<input type="email" class="form-control" id="floatingInputEmail" placeholder="name#example.com" />
<label for="floatingInputEmail">Email address</label>
</div> */}
<hr />
<div class="form-floating mb-3">
<input type="password"
class="form-control"
onChange={passwordChangeHandeler}
id="floatingPassword" placeholder="Password" />
<label for="floatingPassword">Password</label>
</div>
<div class="form-floating mb-3">
<input type="password" class="form-control"
onChange={confirmPasswordChangeHandeler}
id="floatingPasswordConfirm" placeholder="Confirm Password" />
<label for="floatingPasswordConfirm">Confirm Password</label>
</div>
<div>
<label>Select Logo </label>
<input name='image' onChange={imageChangeHandeler} type="file" class="form-control my-4" id="logo" placeholder="Select Logo " />
</div>
<div class="d-grid mb-2">
<button class="btn btn-lg btn-primary btn-login fw-bold text-uppercase" type="submit">Register</button>
</div>
<a class="d-block text-center mt-2 small" >Have an account?<Link class="nav-link" to={'/login'}> Sign In</Link></a>
<hr class="my-4" />
<div class="d-grid mb-2">
<button class="btn btn-lg btn-google btn-login fw-bold text-uppercase" type="submit">
<i class="fab fa-google me-2"></i> Sign up with Google
</button>
</div>
<div class="d-grid">
<button class="btn btn-lg btn-facebook btn-login fw-bold text-uppercase"
// onClick={onSubmitHandeler}
type="submit">
<i class="fab fa-facebook-f me-2"></i> Sign up with Facebook
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
)
}
export default SignUp;
CONTEXT API
import React, { useState } from "react";
import AuthContext from "./AuthContext";
const AuthContextProvider = (props) => {
const [token, setToken] = useState(null);
const login = (loginDetails) => {
}
const signUp = (signUpDetails) => {
console.log("Sign Up Called ");
fetch('http://localhost:5000/register',
{
method:'POST',
body:signUpDetails
// body:JSON.stringify(signUpDetails),
// headers:{
// 'Content-Type': 'application/json'
// }
}).then((resp) => {
return resp.json()
}).then((data) => {
console.log(data);
return data;
}).catch((err) => {
console.log(err);
})
}
const logout = () => {
}
const values = {
login: login,
signup: signUp,
logout: logout,
token: {
token: token,
setToken: setToken
},
isLoggedIn: !!token
}
return (
<div>
<AuthContext.Provider value={values}>
{props.children}
</AuthContext.Provider>
</div>
)
}
export default AuthContextProvider;
But When AT Node server End All Other filed Is recieved as Empty Except image Data
OUTPUT OF DATA SAVED IN DATABASE
And If I follow the Approach in Which I use Simple Object as the data instead of form Data(), I and with header ( that are under // ) I do not receive images at the backend and only receive user info
One solution could be to send the image as a string (base64) to the backend to save the image.
try to implement this in your react:
import { useState } from "react";
import "./styles.css";
export default function App() {
const [img, setImg] = useState("Image string will come here");
const handleChangeImg = (e) => {
console.log(e.target.files[0]);
const reader = new FileReader();
reader.readAsDataURL(e.target.files[0]);
reader.onloadend = () => {
setImg(reader.result);
};
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<input onChange={handleChangeImg} type="file" name="image" />
<button onClick={()=>setImg("Image string will come here")}>Reset</button>
<h1>{img}</h1>
</div>
);
}
Check the code iin codesandbox here.
Related resources:
FileReader

How do I make a button trigger a file input onChange in React?

I have an input of type file. For some reason it doesn't let me change the value attribute to it, and it looks ugly. I swapped it with a button, but now I need the button to somehow trigger the input file on click. How can I do this in React?
Edit:
It should trigger the input onClick, not the onChange like the title says. Unless it's the same thing in this case.
const fireInput = () => {
let input = document.getElementById('inputFile');
}
<div>
<input
id="inputFile"
type='file'
className='mt-2 mb-3 text-primary'
onChange={uploadProfilePic}
/>
<button
type='button'
className='btn btn-primary py-2 px-5 mb-3'
onClick={fireInput}
>
Upload Picture
</button>
</div>
You shouldn't put display:none then the element will not be in dom, you need to use opacity:0 or visibility css.
Code for doing above can be done like this:
import "./styles.css";
import { useRef } from "react";
export default function App() {
const fileUpload = useRef(null);
const uploadProfilePic = (e) => {
console.log(e);
};
const handleUpload = () => {
console.log(fileUpload.current.click(), "fileUpload");
};
return (
<div className="App">
<input
type="file"
ref={fileUpload}
onChange={uploadProfilePic}
style={{ opacity: "0" }}
/>
<button onClick={() => handleUpload()}>Upload Picture</button>
</div>
);
}
You can simply use a label:
.d-none {
display: none;
}
.button {
background-color: #123456;
color: white;
padding: 15px 32px;
text-align: center;
}
<label for="inputFile" class="button">Upload</label>
<input type="file" id="inputFile" name="inputFile" class="d-none">
You could use the html label element without using any JS:
const Component = () => {
// You should also use a ref instead of document.getElementById
const inputRef = useRef()
return (
<label>
<div> {/*Style this however you want*/}
Upload Photo
</div>
<input type="file" style={{display: "none"}} ref={inputRef} />
</label>
)
}
I think you can do like this.
<div>
<input type="file" onChange={uploadProfilePic} ref={(ref) => this.upload = ref} style={{ display: 'none' }}/>
<button
type='button'
className='btn btn-primary py-2 px-5 mb-3'
onClick={(e) => this.upload.click()}
>
Upload Picture
</button>
</div>
You can confirm here.
https://codesandbox.io/s/youthful-cdn-lpntx

Categories

Resources