ReactJS - render file name in text when selected file - javascript

Creating app to upload files from local directory, it was a successful for me. Until the part where I start add some styles and features, I've encounter some issues. after creating connection between <input> + <label>.
<input
id="file"
type="file"
name="selectedFile"
onChange={this.onChange}
/>
<label htmlFor="file">{file}</label>
The goal was to have text render on label tag instead of actual input following onChange event function.
Following ternary conditional with the fileName (from state) and file, they both are set at null as default. Since no file is been selected, the condition is set at false and text "Choose a file" will display on <label>.
render() {
const { fileName } = this.state;
let file = null;
file = fileName
? ( <span>File Selected - {fileName[0]}</span>)
: ( <span>Choose a file...</span> );
Anytime user select on <label> painted "Choose a file" text. it triggers onChange function to have file directory pop up at front of browser. Once file is selected from list, the condition becomes true. And should paint actual file name on <label> eg something.jpg...
<label htmlFor="file">{file}</label>
I didn't get any, it wasn't successful for me... However, I've strong suspect it has something to do with this syntax fileName[0]...
<span>File Selected - {fileName[0]}</span>)
I may have it wrong. Any suggestions? Thanks in advance
Here's full syntax...
export default class Form extends Component {
state = {
fileName: '',
};
onChange = e => {
switch (e.target.name) {
case 'fileName':
this.setState({ fileName: e.target.files[0] });
break;
default:
this.setState({ [e.target.name]: e.target.value });
}
};
render(){
const { fileName } = this.state;
let file = null;
file = fileName
? ( <span>File Selected - {fileName[0]}</span>)
: ( <span>Choose a file...</span> );
return(
<form onSubmit={this.onSubmit}>
<div>
<input
id="file"
type="file"
name="selectedFile"
onChange={this.onChange}
/>
<label htmlFor="file">{file}</label>
</div>
</form>
);
}
}

If I understand your question correctly, then it seems you're wanting the <label /> to display the filename of the file that was selected by the user?
This can be achieved by using the .name property on the file asscoaited with the input's change event:
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {
fileName: '',
};
}
onChange = e => {
switch (e.target.name) {
// Updated this
case 'selectedFile':
if(e.target.files.length > 0) {
// Accessed .name from file
this.setState({ fileName: e.target.files[0].name });
}
break;
default:
this.setState({ [e.target.name]: e.target.value });
}
};
render(){
const { fileName } = this.state;
let file = null;
file = fileName
? ( <span>File Selected - {fileName}</span>)
: ( <span>Choose a file...</span> );
return(
<form onSubmit={this.onSubmit}>
<div>
<input
id="file"
type="file"
name="selectedFile"
onChange={ (event) => this.onChange(event) }
/> { /* Updated this to an arrow function */ }
<label htmlFor="file">{file}</label>
</div>
</form>
);
}
}
For a working example, see this jsFiddle - hope that helps!

Related

Need to add one input field in file upload component

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
);

Can't update multiple state properties at the same time on my React App

I do this project from this course and I want to increase the possibilities of the app by giving the user the ability to insert a birthday person by putting the name, age and photo with a HTML form. In order to do so i use this:
import React, { useState } from 'react'
import ReactDOM from 'react-dom'
class MyForm extends React.Component {
constructor(props) {
super(props)
this.state = {
username: '',
age: null,
filename: null,
}
}
myChangeHandler = (event) => {
event.nativeEvent.stopImmediatePropagation()
let nam = event.target.name
let val = event.target.value
console.log('click')
if (nam === 'age') {
if (!Number(val)) {
alert('Your age must be a number')
}
}
this.setState({
[nam]: val,
file: URL.createObjectURL(event.target.files[0]),
})
}
render() {
return (
<div className="form">
<form>
<h1 className="title">
Today is the Birtday of {this.state.username} of {this.state.age}{' '}
Years Old
</h1>
<img src={this.state.file} alt="Birtday Pic" />
<p>Enter the name of the Birtday Person:</p>
<input type="text" name="username" onChange={this.myChangeHandler} />
<p>Enter his/her age:</p>
<input type="text" name="age" onChange={this.myChangeHandler} />
<p>Click on the chosen button to send the birtday pic</p>
<input
type="file"
id="myFile"
name="filename"
onChange={this.myChangeHandler}
></input>
<button
type="button"
onClick={() => document.getElementById('myFile').click()}
className="send"
>
Send the Birtday Picture
</button>
<input type="submit" value="Submit"></input>
</form>
</div>
)
}
}
export default MyForm
But here is the issue,I can insert the image but when I tring to put name or age this happends:
I am just a rookie with React, maybe this is some silly thing, maybe I don't use well setState(), but I can't see the issue right now
You're using the same setState for every change:
this.setState({
[nam]: val,
file: URL.createObjectURL(event.target.files[0]),
})
HOWEVER, not every event will have .files - only the change handler used by the file input.
I would make a separate change handler for the file input. Have one for text inputs, and one for file inputs.
For example, for file inputs:
myFileChangeHandler = (event) => {
event.nativeEvent.stopImmediatePropagation()
let nam = event.target.name
let val = event.target.value
this.setState({
[nam]: val,
file: URL.createObjectURL(event.target.files[0]),
})
}
and this for text inputs:
myTextChangeHandler = (event) => {
event.nativeEvent.stopImmediatePropagation()
let nam = event.target.name
let val = event.target.value
if (nam === 'age') {
if (!Number(val)) {
alert('Your age must be a number')
}
}
this.setState({
[nam]: val,
})
}

Conditional rendering on select

I am pretty new to the wonderful world of React.
I have two inputs passing data through from an API that renders a list of options. And I want to send the selected inputs from those options back to the parent in the input fields to display for another search.
I have tried passing state down to them and render them them optionally with both a ternary and an if else statement in the "SearchCityList" component in several ways but I either get both lists rendered and they would have to choose between one list that is doubled to put in each input field or it only puts the selected value in one input. Would appreciate any & all suggestions Thanks!
class Form extends Component {
state = {
showComponent: false,
showComponent2: false,
};
// open/close control over SearchCity component box
openSearch = () => {
this.setState({ showComponent: true });
};
openSearch2 = () => {
this.setState({ showComponent2: true });
};
closeSearch = () => {
this.setState({
showComponent: false,
showComponent2: false
});
};
// Passed down cb function to get selected city search in selectCity component
GoingTo = (flights) => {
this.setState({ GoingTo: [flights] });
};
LeavingFrom = (flights) => {
this.setState({ LeavingFrom: [flights] });
};
render() {
return (
<div>
<form className="form-fields container">
<div className="inputs">
<h1>Search for a flight!</h1>
<div className="depart">
<input
onClick={this.openSearch}
className="flight-search"
placeholder="Leaving From"
value={this.state.LeavingFrom}
></input>
<input type="date"></input>
</div>
<div className="Returning">
<input
onClick={this.openSearch2}
className="flight-search"
placeholder="Going To "
value={this.state.GoingTo}
></input>
<input type="date" placeholder="Returning"></input>
</div>
</div>
<button>Check Flights!</button>
</form>
{this.state.showComponent || this.state.showComponent2 ? (
<SearchCity
openSearch={this.openSearch}
openSearch2={this.openSearch2}
flightSearch={this.state.flightSearch}
closeSearch={this.closeSearch}
GoingTo={this.GoingTo}
LeavingFrom={this.LeavingFrom}
onSearchSubmission={this.onSearchSubmission}
closeSearch={this.closeSearch}
/>
) : null}
</div>
);
}
}
export default Form;
class SearchCity extends Component {
state = {
LeavingFrom: "",
GoingTo: "",
search: "",
flightSearch: [],
};
// Search submission / api call
onSearchSubmission = async (search) => {
const response = await Axios.get(
{
headers: {
"
useQueryString: true,
},
}
);
// set New state with array of searched flight data sent to searchCity component
const flightSearch = this.setState({ flightSearch: response.data.Places });
};
// Callback function to send search/input to parent "Form" component
submitSearch = (e) => {
e.preventDefault();
this.onSearchSubmission(this.state.search);
};
// closeSearch callback function sent from Form component to close pop up search box when X is pressed
closeSearch = () => {
this.props.closeSearch();
};
render() {
return (
<div className="container search-list">
<form onChange={this.submitSearch}>
<i className="fas fa-times close-btn" onClick={this.closeSearch}></i>
<input
onChange={(e) => this.setState({ search: e.target.value })} //query-search api
value={this.state.search}
className="search-input"
type="text"
placeholder="Search Locations"
></input>
<div className="search-scroll">
<SearchCityList
openSearch={this.props.openSearch}
openSearch2={this.props.openSearch2}
LeavingFrom={this.props.LeavingFrom}
GoingTo={this.props.GoingTo}
flightSearch={this.state.flightSearch}
/>
</div>
</form>
</div>
);
}
}
export default SearchCity;
function SearchCityList({ flightSearch, LeavingFrom, GoingTo }) {
const renderList = flightSearch.map((flights) => {
return (
<div>
<SelectCityLeaving LeavingFrom={LeavingFrom} flights={flights} />
<SelectCityGoing GoingTo={GoingTo} flights={flights} />
</div>
);
});
return <div>{renderList}</div>;
}
export default SearchCityList;
First of all, when dealing with state, make sure you initialize in the constructor and also ensure you bind your handlers to this component instance as this will refer to something else in the handlers if you don't and you won't be able to call this.setState().
constructor(props) {
super(props); // important
state = {
// your state
};
// make sure to bind the handlers so `this` refers to the
// component like so
this.openSearch = this.openSearch.bind(this);
}

react upload multiple files

I am trying to get my form to upload several files, but once I upload the first one, I have no chance to load a second one. Any Idea what I am doing wrong?
This is my upload component:
import React, { Component } from 'react'
import * as RB from 'react-bootstrap'
import Button from 'components/Button/Button'
class uploadMob extends Component {
constructor(props) {
super(props)
this.state = {
files: [],
}
}
onFilesAdded = (e) => {
const filesArray = this.state.files
filesArray.push(e.target.files[0])
this.setState({ files: filesArray })
this.uploadFiles()
}
async uploadFiles() {
this.state.files.forEach((file) => {
this.sendRequest(file)
})
}
sendRequest(file) {
const { pdfUploadToState } = this.props
pdfUploadToState(file)
}
render() {
const files = this.state.files
return (
<RB.Form.Group>
<div className="upload-btn-wrapper">
<div className="Files">
{files.map((file, key) => {
return (
<div key={key} className="Row">
<span className="Filename">
{file.name}
</span>
</div>
)
})}
</div>
<Button size="sm" variant="light">
Dateien hochladen
</Button>
<input
type="file"
name="files"
id="files"
onChange={(e) => {
this.onFilesAdded(e)
}}
multiple
/>
</div>
</RB.Form.Group>
)
}
}
export default uploadMob
The first file is uploaded perfectly, but as mentioned, the button does not respond when trying to upload a second one.
Thanks for the help!
Your code seems correct but when you use input type file with multiple attribute you need to select multiple files and then hit upload button insted of selecting files one by one.
also replace
filesArray.push(e.target.files[0])
with
for (var i = 0; i < files.length; i++)
{
filesArray.push(e.target.files[i]);
}
to upload file one by one
replace
onFilesAdded = (e) =>
{
this.state.files.push(e.target.files[0])
this.uploadFiles()
}
hope this will help you

laravel and react upload file return 500 Internal Server Error

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>
)
}

Categories

Resources