I have this dropZone that is clickable and opens up the file selecting thing. And once a file is selected from your computer it gives a preview of it (if it's an image). I would like to be able to drag and drop the same file and it do the same thing as if I were to click in the div and open up a file.
I tried calling my handleChange function inside of the onDrop but it didn't quite work and I would get an TypeError: Cannot read property 'target' of undefined error or it just wouldn't do anything at all. Here is a CodeSandbox of the dropzone component on it's own.
Here's that snippet of my code:
class DropZone extends Component {
constructor(props){
super(props)
this.state = {
file: "",
fileId: uuid(),
className: 'dropZone'
}
this.handleChange = this.handleChange.bind(this)
this._onDragEnter = this._onDragEnter.bind(this);
this._onDragLeave = this._onDragLeave.bind(this);
this._onDragOver = this._onDragOver.bind(this);
this._onDrop = this._onDrop.bind(this);
}
handleChange(event) {
this.setState({
file: URL.createObjectURL(event.target.files[0])
})
console.log("handleChange")
//document.getElementsByClassName("dropZone").style.backgroundImage = 'url(' + this.state.file + ')';
}
componentDidMount() {
window.addEventListener('mouseup', this._onDragLeave);
window.addEventListener('dragenter', this._onDragEnter);
window.addEventListener('dragover', this._onDragOver);
document.getElementById('dragbox').addEventListener('dragleave', this._onDragLeave);
window.addEventListener('drop', this._onDrop);
}
componentWillUnmount() {
window.removeEventListener('mouseup', this._onDragLeave);
window.removeEventListener('dragenter', this._onDragEnter);
window.addEventListener('dragover', this._onDragOver);
document.getElementById('dragbox').removeEventListener('dragleave', this._onDragLeave);
window.removeEventListener('drop', this._onDrop);
}
_onDragEnter(e) {
e.stopPropagation();
e.preventDefault();
return false;
}
_onDragOver(e) {
e.preventDefault();
e.stopPropagation();
return false;
}
_onDragLeave(e) {
e.stopPropagation();
e.preventDefault();
return false;
}
_onDrop(e, event) {
e.preventDefault();
this.handleChange
let files = e.dataTransfer.files;
console.log('Files dropped: ', files);
// Upload files
//this.handleChange.bind(this)
console.log(this.state.file)
return false;
}
render() {
const uniqueId = this.state.fileId;
return (
<div>
<input type="file" id={uniqueId} name={uniqueId} class="inputFile" onChange={this.handleChange}/>
<label htmlFor={uniqueId} value={this.state.file}>
{this.props.children}
<div className="dropZone" id="dragbox" onChange={this.handleChange}>
Drop or Choose File
<img src={this.state.file} id="pic" name="file" accept="image/*"/>
</div>
</label>
<div>
</div>
</div>
);
}
}
There's going to be multiple of these that upload their own individual photo/file. Thanks!
You needed to pass on the e parameter to handleChange() and correctly grab the files from the event.
onDrop
_onDrop(e, event) {
e.preventDefault();
this.handleChange(e.dataTransfer.files[0]);
let files = e.dataTransfer.files;
console.log("Files dropped: ", files);
// Upload files
console.log(this.state.file);
return false;
}
handleChange
handleChange(file = "") {
this.setState({
file: URL.createObjectURL(file)
});
//document.getElementsByClassName("dropZone").style.backgroundImage = 'url(' + this.state.file + ')';
}
And in render do parsing of the event to get the file:
onChange={e => this.handleChange(e.target.files[0])}
Alternatively, you could make two handleChange to clean mess up your render method.
Link to working example: https://codesandbox.io/s/7k6y94k6w6 (based on your code)
Related
I'd like to know why I'm getting a 500 error when trying to upload photos to the DB. I have a feeling my controller's messed up as well as my axios call in my React code. Pastebin's below. If you need more information please let me know.
https://pastebin.com/Pv1eigFK
here is App.js
import React, {Component} from 'react';
import axios from 'axios';
import Feed from '../components/Feed/Feed';
import Upload from '../components/Upload/Upload';
import ImagePreview from './ImagePreview/ImagePreview';
class App extends Component {
constructor(props) {
super(props);
this.state = {
selectedFile: null,
previewImgURL: '',
imgPrev: false,
success: false,
progress: 0,
imageChosen: false,
pictures: [],
hideForm: true,
};
this.imageUpload = this.imageUpload.bind(this);
this.submitImageAndRedirect = this.submitImageAndRedirect.bind(this);
this.postIsClicked = this.postIsClicked.bind(this);
this.feedView = this.feedView.bind(this);
}
imagePreview(newPostImageBool) {
this.setState({imgPrev: newPostImageBool});
if (this.state.selectedFile === null) {
alert("can't preview a picture on this because it's empty");
this.setState({imgPrev: false});
}
};
closeModal() {
this.setState({imgPrev: false});
};
imageUpload(e) {
let reader = new FileReader();
let file = e.target.files[0];
reader.onloadend = () => {
this.setState({
selectedFile: file,
previewImgURL: reader.result,
pictures: [reader.result]
}, () => {
console.log(this.state.pictures);
})
};
if (file) reader.readAsDataURL(file); // Allows user to preview image uploaded
this.setState(() => ({file}));
this.setState({success: true, imageChosen: true});
}
submitImageAndRedirect() {
// e.preventDefault();
let picUrl = this.state.previewImgURL;
axios.post('/home', {
body: picUrl
}).then(response => {
// console
console.log(response);
// set state
this.setState({
pictures: [picUrl, response.data]
});
});
console.log("submitImageAndRedirect() triggered");
}
postIsClicked(e) {
console.log("postIsClicked(e) triggered");
if (e.target.value === "Yes") {
this.feedView();
this.submitImageAndRedirect();
console.log(`Yes has been clicked... inside Yes if block`);
} else {
alert("No clicked");
}
}
feedView() {
this.setState({hideForm: false}, () => console.log(this.state.hideForm));
}
render() {
return (
<div className="feed-wrapper">
{this.state.success ?
<div className="alert alert-success">
<strong>Chosen image is successful!
Now click Preview and make sure that's the one you want to upload!</strong>
</div> : null}
{this.state.hideForm ?
<form onSubmit={this.submitImageAndRedirect}>
<div className="inputWrapper">
<input
id="new_post_image"
name="post_image"
className="button is-success is-outlined"
type="file"
style={{display: 'none'}}
onChange={this.imageUpload}
accept="image/*"
/>
<Upload/>
<br/>
{
this.state.imageChosen ?
<div className="uploaded-pics">
<ImagePreview src={this.state.previewImgURL} onClick={this.postIsClicked}/>
</div> : null
}
</div>
</form>
: null
}
{!this.state.hideForm ?
this.state.pictures.map(post => {
return <Feed src={post} />
})
:null}
</div>
);
}
}
export default App;
Here's my controller:
<?php
namespace App\Http\Controllers;
use App\User;
use App\PostPictures;
use Illuminate\Http\Request;
class PostPicturesController extends Controller
{
public function create(Request $request, PostPictures $postPicture) {
$uploadPic = $postPicture->user()->postPictures->create([
'body' => $request->body
]);
return response()->json($postPicture->with('user')->find($uploadPic->id));
}
}
error in console:
POST http://mywebsite.test/home 500 (Internal Server Error)
error in Laravel logs:
[2019-10-06 16:25:56] local.ERROR: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'mywebsite.post_pictures' doesn't exist (SQL: select * from `post_pictures` where `post_pictures`.`user_id` = 5 and `post_pictures`.`user_id` is not null) {"userId":5,"exception":"[object] (Illuminate\\Database\\QueryException(code: 42S02): SQLSTATE[42S02]: Base table or view not found: 1146 Table 'mywebsite.post_pictures' doesn't exist (SQL: select * from `post_pictures` where `post_pictures`.`user_id` = 5 and `post_pictures`.`user_id` is not null) at /Users/garenvartanian/workstation/mywebsite/vendor/laravel/framework/src/Illuminate/Database/Connection.php:664, PDOException(code: 42S02): SQLSTATE[42S02]: Base table or view not found: 1146 Table 'mywebsite.post_pictures' doesn't exist at /Users/garenvartanian/workstation/mywebsite/vendor/laravel/framework/src/Illuminate/Database/Connection.php:326)
[stacktrace]
The error said, laravel can not found you table post_pictures on mywebsite database.
Did you create the table?
if you don't please create a migration for make table:
https://laravel.com/docs/5.8/migrations
sometime you maybe wish make the model name is different with the table name on database.
You may need to add table name to model like :
class Flight extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'my_flights';
}
hope it help. Thanks
I haven't seen a post to answer this question yet, so forgive me if I overlooked something. I'm trying to populate a table with ONLY the nested array from my data on the page. this.state.data SHOULD contain a subCollection of "masterTicketItems" from the "master ticket" that is being into this React JSX script from the Razor View.
The trouble comes when I'm trying to optimistically update a given table, and ONLY focusing on that, rather than actively posting data. I've been trying look around to understand this ReactJS and ES6 "spread" mapping technique, and I'm not sure if that applies here or something different.
In essence, I'm trying to ONLY map a "LogList" component with a collection contained INSIDE "this.state.data" inside the parent "Ticket" component. I currently have if/else logic to detect if there is nothing in the collection yet, and if not, try to populate it with a SINGLE object from the child component that was "posted" from the LogForm component. However, I can't seem to get the page to render with the single child in the table! Here's the Ticket JSX file snippet for review:
class Ticket extends React.Component {
constructor(props) {
super(props);
this.state = { data: this.props.initialData };
this.handleLogSubmit = this.handleLogSubmit.bind(this);
}
loadLogsFromServer() {
const xhr = new xmlhttprequest();
xhr.open('get', this.props.geturl + this.props.id, true);
xhr.onload = () => {
const data = json.parse(xhr.responsetext);
this.setState({ data: data });
};
xhr.send();
}
handleLogSubmit(log) {
const logs = this.state.data.apptConfirmItems;
// Optimistically set an id on the new comment. It will be replaced by an
// id generated by the server. In a production application you would likely
// use a more robust system for ID generation.
if (!logs) {
log.ticketItemId = 1;
const newLogs = log;
let masterItemsAccess = Object.assign({}, this.state.data);
masterItemsAccess.masterTicketItems = log;
this.setState((data) => Object.assign({}, data, { masterTicketItems: [log] }));
}
else {
log.ticketItemId = logs.length + 1;
const newLogs = logs.concat([log]);
this.setState({ data: newLogs });
}
const data = new FormData();
data.append('ItemType', log.itemType);
data.append('ItemDescription', log.text);
const xhr = new XMLHttpRequest();
//xhr.open('post', this.props.submitUrl, true);
//xhr.onload = () => this.loadLogsFromServer();
//xhr.send(data);
}
componentDidMount() {
window.setInterval(() => this.loadLogsFromServer(), this.props.pollInterval);
}
render() {
if (!this.state.data.masterTicketItems || this.state.data.masterTicketItems.Count == 0) {
return (
<div className="queue">
<h1>Affirm Logs</h1>
<h2>{this.state.data.summary}</h2>
<h5><i>{this.state.data.description}</i></h5>
<div><h3>No logs at this time!</h3></div>
<LogForm onLogSubmit={this.handleLogSubmit} />
</div>
);
}
else {
return (
<div className="queue">
<h1>Affirm Logs</h1>
<h2>{this.state.data.summary}</h2>
<h5><i>{this.state.data.description}</i></h5>
<LogList data={this.state.data.masterTicketItems}/>
<LogForm onLogSubmit={this.handleLogSubmit} />
</div>
);
}
}
}
class LogList extends React.Component {
render() {
const logNodes = this.props.data.masterTicketItems.map(log => (
<Log key={log.ticketItemId}>
<td>{log.itemType}</td> < td > {log.itemDescription}</td>
</Log>
));
const logRaw = this.props.data.masterTicketItmes.map(log => (
<tr>
<td>{log.itemType}</td><td>{log.itemDescription}</td>
</tr>
));
return (
<div className="logList">
<table id="affirmTable" className="table table-bordered table-hover table-striped">
<tbody>
{logNodes}
</tbody>
</table>
</div>
);
}
}
class LogForm extends React.Component {
constructor(props) {
super(props);
this.state = { itemType: 'Test Log', itemDescription: '' };
this.handleItemTypeChange = this.handleItemTypeChange.bind(this);
this.handleTextChange = this.handleTextChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleItemTypeChange(e) {
this.setState({ itemType: e.target.value });
}
handleTextChange(e) {
this.setState({ itemDescription: e.target.value });
}
handleSubmit(e) {
e.preventDefault();
const itemType = this.state.itemType.trim();
const itemDescription = this.state.itemDescription.trim();
if (!itemType || !itemDescription) {
return;
}
this.props.onLogSubmit({ ItemType: itemType, ItemDescription: itemDescription });
this.setState({ itemType: '', text: '' });
}
render() {
return (
<form className="logForm" onSubmit={this.handleSubmit}>
<input
type="text"
placeholder="Your name"
value={this.state.itemType}
onChange={this.handleItemTypeChange}
/>
<input
type="text"
placeholder="Enter working log here..."
value={this.state.itemDescription}
onChange={this.handleTextChange}
/>
<input type="submit" value="Post" />
</form>
);
}
}
I purposely left out the child Log component, since I'm confident that when this is addressed that component WILL populate with the table data as expected.
Should I use nested mapping with ES6? Is there a simpler way? I'm trying to understand React state logic a little bit better.
I am fairly new to React. Currently I have two React components - Article.js and ControlForm.js
My render return in Article.js is this:
return (
<div className="article">
{article_wrapper.map( article =>
<div key={article.node.nid} className="article-main-display">
<h1 className="title" dangerouslySetInnerHTML={createMarkup(article.node.title)}/>
<div className="img-div"><img src={article.node.field_image.src} /></div>
<ControlForm />
<div dangerouslySetInnerHTML={createMarkup(article.node.field_user_hsk_level)} />;
<div className="field-name-field-chinese">
<div dangerouslySetInnerHTML={createMarkup(article.node.chinese)} />;
</div>
</div>
)}
</div>
);
The ControlForm.js has several form elements (all of which I'd like to be able to pass along if need be), but this is the main one:
<div className="form-item form-type-select form-group">
<label className="control-label">Font Size</label>
<select
value={this.state.value}
onChange={this.handleSizeSelect}
id="font-size"
className="form-control form-select"
>
<option value="0">Small</option>
<option value="1">Normal</option>
<option value="2">Large</option>
<option value="3">XL</option>
</select>
</div>
I'd like to be able to set a class on one of the divs in the Article.js based on changing a value in the ControlForm.js
What is the most "React" way to do this? Would creating a common parent to both be the best method (right now, their only parent in common is the main App.js)
Sorry if I don't totally understand how this is supposed to work - this is my first React app.
The class associated with the components are ControlForm and withFetching respectively.
EDIT - the demo example below works, but I have some additional issues with how to integrate it properly into my existing code. Here's the existing functions of ControlForm:
class ControlForm extends Component {
constructor() {
super();
this.state = { toggleActive: false, sizeSelect: "0", speed: 1.3, volume: .6};
this.onToggle = this.onToggle.bind(this);
this.handleSpeedChange = this.handleSpeedChange.bind(this);
this.handleVolumeChange = this.handleVolumeChange.bind(this);
this.handleSizeSelect = this.handleSizeSelect.bind(this);
}
onToggle() {
this.setState({ toggleActive: !this.state.toggleActive });
}
handleSizeSelect(event) {
this.setState({ sizeSelect: event.target.value });
this.setState({font: 'large-font'});
parentMethod(event.target.value);
}
handlePlayClick(e) {
e.preventDefault();
voice.playButtonClick();
}
handlePauseClick(e) {
e.preventDefault();
voice.pauseButtonClick();
}
handleStopClick(e) {
e.preventDefault();
voice.stopButtonClick();
}
handleVolumeChange(event) {
console.log(event.target.value);
this.setState({ volume: event.target.value });
}
handleSpeedChange(event) {
console.log(event.target.value);
this.setState({ speed: event.target.value });
}
Articles looks like this:
const withFetching = (url) => (Comp) =>
class WithFetching extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
isLoading: false,
error: null,
dynamicClassName: "parentClass"
};
this.changeClassName = this.changeClassName.bind(this);
}
changeClassName(childData) {
this.setState({
dynamicClassName: childData
});
}
componentDidMount() {
this.setState({ isLoading: true });
fetch(url)
.then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error('Something went wrong ...');
}
})
.then(data => this.setState({ data, isLoading: false }))
.catch(error => this.setState({ error, isLoading: false }));
}
render() {
//return "test";
return <Comp { ...this.props } { ...this.state } />
}
}
function createMarkup(html) {
return {__html: html};
}
function changeClassName(childData) {
console.log("GETS HERE!")
this.setState({
dynamicClassName: childData
});
}
const Articles = ({ data, isLoading, error }) => {
console.log(data);
console.log(isLoading);
const article_wrapper = data.nodes || [];
if (error) {
return <p>{error.message}</p>;
}
if (isLoading) {
return <p>Loading ...</p>;
}
return (
<div className="article">
{article_wrapper.map( article =>
<div key={article.node.nid} className="article-main-display">
<h1 className="title" dangerouslySetInnerHTML={createMarkup(article.node.title)}/>
<div className="img-div"><img src={article.node.field_image.src} /></div>
<ControlForm parentMethod={changeClassName} />
<div dangerouslySetInnerHTML={createMarkup(article.node.field_user_hsk_level)} />;
<div className="field-name-field-chinese">
<div dangerouslySetInnerHTML={createMarkup(article.node.chinese)} />;
</div>
</div>
)}
</div>
);
}
export default withFetching(API)(Articles);
Sorry about all of these questions, I know a lot of this is due to unfamiliarity with React - this is the first thing I've tried to build in React
You want to change parents from his childs.
First, you have to create a handler function at Article.js and pass it to ControlForm.js as a property.
<ControlForm changeDiv={this.changeDiv} />
Then you focus on ControlForm.js, whenever you want to happen, you just execute the function you passed as a the prop changeDiv, like this.props.changeDiv()
See also possible duplicate: How to update parent's state in React?
you can conditionally render a class based on state and your handler was missing the values from the event on the onChange
here's a demo of dynamically changing style base on the state
demo
Article.js ,
class Article extends React.Component {
constructor(props) {
super(props);
this.state = {
dynamicClassName: "parentClass"
}
this.changeClassName = this.changeClassName.bind(this);
}
changeClassName(childData) {
this.setState({
dynamicClassName: childData
});
}
// user dynamicClassName wherever u want .
return ( <
div className = "article" > {
article_wrapper.map(article =>
<
div key = {
article.node.nid
}
className = "article-main-display" >
<
h1 className = "title"
dangerouslySetInnerHTML = {
createMarkup(article.node.title)
}
/> <
div className = "img-div" > < img src = {
article.node.field_image.src
}
/></div >
<
ControlForm parentMethod={this.changeClassName} / >
<
div dangerouslySetInnerHTML = {
createMarkup(article.node.field_user_hsk_level)
}
/>; <
div className = "field-name-field-chinese" >
<
div dangerouslySetInnerHTML = {
createMarkup(article.node.chinese)
}
/>; < /
div > <
/div>
)
} <
/div>
);
}
In ControlForm js ,
class ControlForm extends React.Component {
constructor(props) {
super(props);
this.state = {
}
this.handleSizeSelect= this.handleSizeSelect.bind(this);
}
handleSizeSelect() {
this.props.parentMethod(this.state.value);
}
render() {
return (
<div className="form-item form-type-select form-group">
<label className="control-label">Font Size</label>
<select
value={this.state.value}
onChange={this.handleSizeSelect}
id="font-size"
className="form-control form-select"
>
<option value="0">Small</option>
<option value="1">Normal</option>
<option value="2">Large</option>
<option value="3">XL</option>
</select>
</div>
);
}
}
this.state = {
imageURL: []
};
fileSelectHandler = (event) => {
this.setState({
imageURL: [???]
});
}
<input type="file" multiple onChange={fileSelectHandler} />
<img src={imageURL[0]} />
Here I need to dynamically add multiple img tags according to the number of images selected. Please help!
On File change you can try the sample code here
fileSelectHandler = (e) => {
Array.from(e.target.files).forEach(file => {
const reader = new FileReader();
reader.onload = (e) => {
this.setState({
images: this.state.images.concat(e.target.result),
});
};
reader.readAsDataURL(file);
})
}
A demo is here https://codesandbox.io/s/qxxzz1q11j
I have used react dropzone component for multiple image upload but the image preview is not shown on my template.Files are shown on console.Where have i done wrong? I have used this component from here https://github.com/okonet/react-dropzone . let me know what else i have to provide to examine this issue with fine-tooth comb. Thanks for help in advance. Your help will be appreciated.
The code is
var Gallery = React.createClass({
getInitialState: function () {
return {
files:[]
};
},
onDrop(files) {
console.log('Received files: ', files);
this.setState({
files: files
});
},
showFiles() {
var files = this.state;
console.log('files',files);
if (!files.length) {
return null;
}
return (
<div>
<h3>Dropped files: </h3>
<ul className="col-md-4">
{
files.map((file, idx) => {
return (
<li key={idx}>
<img src={file.preview} width={100}/>
<div>{file.name + ' : ' + file.size + ' bytes.'}</div>
</li>
);
})
}
</ul>
</div>
);
},
render: function () {
return (
<div>
<div className="col-md-12">
<Dropzone onDrop={this.onDrop}>
Try dropping some files here, or click to select files to upload.
</Dropzone>
{this.showFiles()}
</div>
<div className="row">
<button className="btn" onClick={this.submit}>Next</button>
</div>
</div>
);
},
});
The problem is in the showFiles method.
var files = this.state;
//const {files}=this.state
you don't assign the files to the file var, but the whole state.
i managed to solve the problem by doing this
var files = this.state.files || null;