I have one component in which we have file upload facility. I need to add one additioonal input filed so when user clicks on the upload button one input filed and one file should be sent to server.
since its class component I am not able to use hook. its legacy application.
import axios from 'axios';
import React,{Component} from 'react';
class App extends Component {
state = {
// Initially, no file is selected
selectedFile: null
};
// On file select (from the pop up)
onFileChange = event => {
// Update the state
this.setState({ selectedFile: event.target.files[0] });
};
// On file upload (click the upload button)
onFileUpload = () => {
// Create an object of formData
const formData = new FormData();
// Update the formData object
formData.append(
"myFile",
this.state.selectedFile,
this.state.selectedFile.name
);
// Details of the uploaded file
console.log(this.state.selectedFile);
// Request made to the backend api
// Send formData object
axios.post("api/uploadfile", formData);
};
// File content to be displayed after
// file upload is complete
fileData = () => {
if (this.state.selectedFile) {
return (
<div>
<h2>File Details:</h2>
<p>File Name: {this.state.selectedFile.name}</p>
<p>File Type: {this.state.selectedFile.type}</p>
<p>
Last Modified:{" "}
{this.state.selectedFile.lastModifiedDate.toDateString()}
</p>
</div>
);
} else {
return (
<div>
<br />
<h4>Choose before Pressing the Upload button</h4>
</div>
);
}
};
render() {
return (
<div>
<h3>
File Upload using React!
</h3>
<div>
<input type="file" onChange={this.onFileChange} />
<button onClick={this.onFileUpload}>
Upload!
</button>
</div>
{this.fileData()}
</div>
);
}
}
export default App;
I tried a lot but it is not working properly. if you need I can put the modified code. since its quite messy I put only working code without input field.
Could you please help me to add one input field, please.
Edit 1
Modified Code
import React from 'react';
import axios from 'axios';
class FileUpload extends React.Component {
constructor() {
super();
this.state = {
selectedFile: '',
countryCode: '',
responseArray: [],
};
this.handleInputChange = this.handleInputChange.bind(this);
this.handleInput = this.handleInput.bind(this);
}
handleInputChange(event) {
this.setState({
selectedFile: event.target.value,
responseArray: [],
});
}
handleInput(event) {
this.setState({
countryCode: event.target.value,
});
}
handleSubmit() {
if (!this.state.selectedFile) {
alert('Please select The file');
return false;
}
if (!this.state.countryCode) {
alert('Please select The Country Code');
return false;
}
const data = new FormData();
for (let i = 0; i < this.state.selectedFile.length; i++) {
data.append('file', this.state.selectedFile[i]);
}
data.append('countryCode', this.state.countryCode);
console.log(data.countryCode);
let url = process.env.API_URL;
axios.post('http://localhost:8080/file_upload', data, {}).then(
(res) => {
console.log(data);
// this.setState({ responseArray: res.data });
// this.resetFile();
},
(error) => {
alert(error);
}
);
}
resetFile() {
document.getElementsByName('file')[0].value = null;
}
render() {
return (
<form>
<div className="row">
<div className="col-md-12">
<h1>Translation File Upload</h1>
<div className="form-row">
<div className="form-group col-md-8">
<label>Please enter the country code</label>
<input
type="text"
value={this.state.countryCode}
onChange={this.handleInput}
required
/>
</div>
</div>
<div className="form-row">
<div className="form-group col-md-8">
<label>Select File :</label>
<input
type="file"
className="form-control"
multiple
name="file"
onChange={this.handleInputChange}
required
/>
<hr />
</div>
</div>
<br />
<div className="form-row">
<div className="col-md-6">
<button onClick={this.handleSubmit.bind(this)}>Upload </button>
</div>
</div>
<br />
</div>
</div>
</form>
);
}
}
export default FileUpload;
Can you try
<h3>
File Upload using React!
</h3>
<div>
<input type="file" onChange={this.onFileChange} />
<button onClick={this.onFileUpload}>
Upload!
</button>
<input type="text" onChange={this.onInputChange} required>
</div>
and then in your code
inputField: ''
onInputChange = event => {
// Update the state
this.setState({ inputField: event.target.value });
};
// in the formData part
formData.append(
"inputField",
this.state.inputField
);
Related
I already built the form in React and it shows the input fields in red borders that'll change to regular borders once someone types it in. I used this example from this React form article link So everything is working except I wanted to add the error message under the input field that displays "Please fill in the blank field" that will disappear once someone starts typing in the field. How do I do this?
Here's my code in Form.js:
import React, { Component } from 'react';
import FormField from './FormFieldBox';
function validate(name, isin) {
// true means invalid, so our conditions got reversed
return {
name: name.length === 0,
isin: isin.length === 0
};
}
export default class PopupForm extends Component {
constructor(props) {
super(props)
this.state = {
name: '',
isin: '',
country: '',
errormessage: ''
}
}
updateInput = (e) =>{
this.setState({[e.target.name]: e.target.value})
}
closePopupSubmit = (e) => {
if (!this.canBeSubmitted()) {
e.preventDefault();
}
let security = { //1.gather security data from form submit
name: this.state.name,
isin: this.state.isin,
country: this.state.country
}
this.props.submitPopup(security); //2.closePopup function, add security data
}
canBeSubmitted() {
const errors = validate(this.state.name, this.state.isin);
const isDisabled = Object.keys(errors).some(x => errors[x]);
return !isDisabled;
}
cancelPopupSubmit = (e) => {
e.preventDefault()
this.props.cancelPopup();
}
render() {
const errors = validate(this.state.name, this.state.isin);
const isDisabled = Object.keys(errors).some(x => errors[x]);
return (
<div className='popup'>
<div className='popup-inner'>
<form onSubmit={this.closePopupSubmit}>
<FormField onChange={this.updateInput} className={errors.name ? "input error" : "input"} label="Name" type="text" name="name" value={this.state.name} />
<FormField onChange={this.updateInput} className={errors.isin ? "input error" : "input"} label="ISIN" type="text" name="isin" value={this.state.isin} />
<FormField onChange={this.updateInput} label="Country" type="text" name="country" value={this.state.country} />
<button type="button" onClick={this.cancelPopupSubmit} className="button">Cancel</button>
<button type="submit" className="button" disabled={isDisabled}>Submit</button>
</form>
</div>
</div>
)
}
}
And my component FormField.js
import React from "react";
const FormBox = props => {
return (
<div className="field">
<label className="label">{props.label}</label>
<div className="control">
<input onChange={props.onChange}
className={props.className}
type={props.type}
name={props.name}
value={props.value}
placeholder={props.placeholder} />
{/* {props.errormessage} */}
</div>
</div>
)
}
export default FormBox;
const FormBox = props => {
return (
<div className="field">
<label className="label">{props.label}</label>
<div className="control">
<input onChange={props.onChange}
className={props.className}
type={props.type}
name={props.name}
value={props.value}
placeholder={props.placeholder} />
</div>
{Boolean(props.value.length) || (
<div className="err-msg">
Please fill in the blank field
</div>
)}
</div>
)
}
There are two ways you can achieve this
First : oninvalid attribute in HTML5 and calling a custom function on that.
Second : along with each element name object in state have a length attribute. In validation function you can check for the length and throw a custom error that you want to display.
I want to add some input lines by a click of a button and add it to state so I can send it to the server, but I'm not sure how to add it to the fetch method or even if it's added to state,
this is what i have so far:
export class AdminPage extends React.Component {
constructor(props){
super(props);
this.state = {
sendeEmail: '',
matrialeliste: [{
matrialer: '',
antal: '',
pris: ''}]
};
}
handleUserInput = (e) => {
if (["matrialer", "antal", "pris"].includes(e.target.className) ) {
let matrialeliste = [...this.state.matrialeliste]
//matrialeliste[e.target.dataset.id][e.target.className] = e.target.value
this.setState({ matrialeliste }, () => console.log(this.state.matrialeliste))
} else {
const name = e.target.name;
const value = e.target.value;
this.setState({[name]: value};
}
}
addMatrialeliste = (e) => {
this.setState((prevState) => ({
matrialeliste: [...prevState.matrialeliste, {matrialer:"", antal:"", pris:""}],
}));
}
onSubmitSignIn = (event) => {
event.preventDefault();
fetch(`${api.url}/form`, {
method: 'post',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
sendeEmail: this.state.sendeEmail,
})
})
.then((response) => (response.json()))
.catch(error => console.log(error));
}
render(){
let {matrialeliste} = this.state;
return(
<div>
<div>
<h1>Arbejds seddel</h1>
<form>
<div>
<button type="button" onClick={this.addMatrialeliste}>
tilføj materialer
</button>
{
matrialeliste.map((val, idx) => {
return(
<div key={idx}>
<div>
<label htmlFor="matrialer">
Matrialeliste
</label>
<input name='matrialer' type="text" className='matrialer' onChange={this.handleUserInput} />
</div>
<div>
<label htmlFor="antal">
Antal
</label>
<input name='antal' type="number" className='antal' onChange={this.handleUserInput} />
</div>
<div>
<label htmlFor="pris">
Pris
</label>
<input name='pris' type="number" className='pris' onChange={this.handleUserInput} />
</div>
</div>)})}
<label htmlFor="email">
E-mail
</label>
<input name='email' type="email" onChange={e => this.handleUserInput} />
<button type="submit">Send som E-mail</button>
<div>
<button type="submit" disabled=this.state.formValid}>Create</button>
</div>
</div>
</form>
</div>
</div>
);
}
}
I can get to add extra lines, but I don't know how to add it to the fetch method.
I was thinking I could map it, but I'm still unsure how do that
Creating forms with plain React requires you to write each part of the process and for a complex state its become tough .so, my personal opinion is to go with Formik or React Hook Form as they cover most of the features.
In your case, I am assuming you want to sent whole state to fetch method .here is an example of your code which implemented with Formik library.
i have a problem with my code when I try to upload a file with reactjs and laravel for backend, after click submit button server response with 500 IS error,looking forward inside code i get message Call to a member function getClientOriginalName() on null, I guess error appear because of my js function, so hopefully someone can help me finger it out, i am appreciate it
my react component :
constructor(){
super();
this.fileInput = React.createRef();
this.state = {
name: '',
avatar: '',
}
}
handleNameChange(e){
this.setState({
name: e.target.value
})
}
handleAvatarChange(e){
this.setState({
avatar:this.fileInput.current.files[0].name
})
}
handleSubmit(e){
e.preventDefault();
axios.post('/api/add', this.state).then(response => {
console.log(response)
}).then(error => {
console.log(error)
})
}
render(){
return (
<div>
home
{/*<Link to="/" className="btn btn-success">Return to Items</Link>*/}
<form className="form-horizontal" onSubmit={this.handleSubmit.bind(this)}>
<input type="text" className="form-control" id="name" value={this.state.name} onChange={this.handleNameChange.bind(this)}/>
<input type="file" ref={this.fileInput} onChange={this.handleAvatarChange.bind(this)} /><br></br>
<button type="submit" className="btn btn-default">Save</button>
</form>
</div>
)
}
}
my controller:
public function store(Request $request){
$file_name_image = $request->file('avatar')->getClientOriginalName();
$user = new User();
$user->name = $request->name;
$user ->avatar = $file_name_image;
$request->file('avatar')->move('upload/',$file_name_image);
$user->save();
return response()->json('Successfully added');
}
I think the problem in your input tag.
Input doesn't have name attribute. You should add it.
<input name="file" type="text" className="form-control" id="name" value={this.state.name} onChange={this.handleNameChange.bind(this)}/>
And I recommend you add IF statement in yours controller.
if ($request->hasFile('file')) {
//
}
I can not comment, so I will write the answer here.
So you can find the answer for your original question here: How do I set multipart in axios with react?
If you want to send other data than file too, you just simply add it like you did with the file. for example:
formData.append('name', 'Your new name')
first parameter is the name you want for the POST data key, the second one is the data you want to send. It can come from the state etc.
for anyone else who may need, thank all!
constructor(props) {
super(props);
this.state ={
file:null,
name:'',
}
this.onFormSubmit = this.onFormSubmit.bind(this)
this.onChange = this.onChange.bind(this)
this.fileUpload = this.fileUpload.bind(this)
this.handleNameChange = this.handleNameChange.bind(this)
}
onFormSubmit(e){
e.preventDefault() // Stop form submit
this.fileUpload(this.state.file,this.state.name).then((response)=>{
console.log(response.data);
})
}
onChange(e) {
this.setState({file:e.target.files[0]})
}
handleNameChange(e){
this.setState({
name: e.target.value
})
}
fileUpload(file, name){
const url = 'http://localhost:8000/api/add';
const formData = new FormData();
formData.append('file',file);
formData.append('name', name);
const config = {
headers: {
'content-type': 'multipart/form-data'
}
}
return post(url, formData,config)
}
render() {
return (
<form onSubmit={this.onFormSubmit}>
<h1>File Upload</h1>
<input type="text" value={this.state.name} onChange={this.handleNameChange}/>
<input type="file" onChange={this.onChange} />
<button type="submit">Upload</button>
</form>
)
}
First, I am not a english native. So, my sentence can be wrong grammatically or can be difficult to convey meaning.
I have a React Project with Laravel and Axios. I want to upload image data on my server. So, I tried set file data in state when file is uploaded to follow this document, but it doesn't work.
I want to know reason why it isn't work.
Here is some of my code.
import Axios from 'axios';
import React, { Component } from 'react';
class Register extends Component {
constructor() {
super();
this.state = {
name: '',
email: '',
password: '',
password_confirmation: '',
profile_picture: null,
errors: [],
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.hasErrorFor = this.hasErrorFor.bind(this);
this.renderErrorFor = this.renderErrorFor.bind(this);
}
handleChange(event) {
if(event.target.name.match('profile_picture')) {
// when input profile picture
this.setState({
[event.target.name]: event.target.files[0],
});
console.log(event.target.files[0]);
} else {
// when input text values
this.setState({
[event.target.name]: event.target.value,
});
}
console.log(this.state);
}
handleSubmit(event) {
// submit event
}
hasErrorFor(field) {
// detect error
}
renderErrorFor(field) {
// render error message
}
render() {
return (
<div className="container">
<div className="row justify-content-center">
<div className="col-md-8">
<div className="card">
<div className="card-header">Register</div>
<div className="card-body">
<form
action="/api/register"
method="post"
encType='application/x-www-form-urlencoded'
onSubmit={this.handleSubmit}
>
<div className="form-group">
// input name
</div>
<div className="form-group">
// input email
</div>
<div className="form-group">
// input password
</div>
<div className="form-group">
// input password confirmation
</div>
<div className="form-group">
<label htmlFor="profile_picture">Profile Picture</label>
<input
id="profile_picture"
type="file"
name="profile_picture"
className='form-control-file'
onChange={this.handleChange}
/>
{this.renderErrorFor('profile_picture')}
</div>
<button className="btn btn-primary">SUBMIT</button>
</form>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default Register;
And here is result of this code.
I except the uploaded file is set in state when I upload a file, but file doesn't set in state.
you've written console.log() immediately after setState method, since setState is an async method an at the time java script runs your console.log it will be showing you the previous state in which obviously image object is not set, for this you should put your console in the second argument of setState that is a callback function.
this.setState({[event.target.name]: event.target.files[0]},()=>console.log(this.state));
I'm coding an image uploader in my admin interface. I have a form that is made for creating new meals that include several properties (name, description, etc.) but also an image that I upload and store thanks to Cloudinary.
I want a thumbnail of my image to appear once I dropped the image inside the dropzone. So I added a ternary operator that should render the thumbnail once the image is uploaded.
However, this piece of code does not re-render once the image is uploaded. the div remains empty and the thumbnail does not appear.
is there something wrong in my code ??
import React from 'react';
import Dropzone from 'react-dropzone'
import axios from 'axios'
export default class MealForm extends React.Component {
constructor() {
super();
this.state = {
mealImageURL: ""
}
this.createMeal = this.createMeal.bind(this);
}
handleDrop(files) {
const uploaders = files.map(file => {
const formData = new FormData();
formData.append("file", file);
formData.append("tags", `meal, food`);
formData.append("upload_preset", "xxxxxxx");
formData.append("api_key", "xxxxxxxxxx");
formData.append("timestamp", (Date.now() / 1000) | 0);
// Make an AJAX upload request using Axios (replace Cloudinary URL below with your own)
return axios.post("https://api.cloudinary.com/v1_1/xxxxxxx/image/upload", formData, {
headers: { "X-Requested-With": "XMLHttpRequest" },
}).then(response => {
const data = response.data;
const fileURL = data.secure_url // You should store this URL for future references in your app
this.setState({mealImageURL: fileURL});
console.log(this.state.mealImageURL);
console.log(data);
})
});
// Once all the files are uploaded
axios.all(uploaders).then(() => {
// ... perform after upload is successful operation
});
}
createMeal(e) {
e.preventDefault();
let name = this.refs.name.value.trim();
let description = this.refs.description.value.trim();
let ingredients = this.refs.ingredients.value.trim();
let allergenes = this.refs.allergenes.value.trim();
let category = this.refs.category.value.trim();
let weekDay = this.refs.weekday.value.trim();
let restaurant = this.refs.restaurant.value.trim();
let image = this.state.mealImageURL;
Accounts.createUser({}, err => {
console.log('Meal creation Callback: ', err)
})
}
render() {
return (
<form onSubmit={this.createMeal}>
<input type="text" ref="name" className="form-control" id="meal-form-name-input" aria-describedby="name" placeholder="Name" />
<textarea ref="description" className="form-control" id="meal-form-description-input" aria-describedby="description" placeholder="description" rows="3"></textarea>
<textarea ref="ingredients" className="form-control" id="meal-form-ingredients-input" aria-describedby="ingrdients" placeholder="ingredients" rows="2"></textarea>
<textarea ref="allergenes" className="form-control" id="meal-form-allergenes-input" aria-describedby="allergenes" placeholder="allergenes" rows="2"></textarea>
<input type="text" ref="category" className="form-control" id="meal-form-category-input" aria-describedby="category" placeholder="category" />
<input type="text" ref="weekday" className="form-control" id="meal-form-weekday-input" aria-describedby="week day" placeholder="week day" />
<input type="text" ref="restaurant" className="form-control" id="meal-form-restaurant-input" placeholder="restaurant" />
<div>
<div className="FileUpload">
<Dropzone
onDrop={this.handleDrop}
multiple={false}
accept="image/*"
>
<p>Drop your files or click here to upload</p>
</Dropzone>
</div>
<div> // That's my ternary operator:
{this.state.mealImageURL === '' ? null :
<div>
<p>{this.state.mealImageURL}</p>
<img src={this.state.mealImageURL} />
</div>}
</div>
</div>
<button type="submit" className="btn btn-primary">Create Meal</button>
</form>
);
}
}
you forgot this.handleDrop = this.handleDrop.bind(this); In the constructor.