No access to path key on file upload - javascript

I am trying to get access to HTMLElementInput.files.path to send to my backend for cloudinary to process. The problem I am running into is there is now path key present on the object.
I've been reading through the MDN Input page, and I'm pretty sure I'm not doing anything wrong with the markup. The below is the code I have for intercepting the data on the input:
class App extends Component {
capture(e) {
e.preventDefault();
const image = document.querySelector('input').files[0];
console.log(image);
// image.path === undefined
}
render() {
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2>Welcome to React</h2>
</div>
<form onSubmit={e => this.capture(e)}>
<div>
<label htmlFor="photo_image">Upload an image</label>
<input id="photo_image" type="file" accept=".jpg, .jpeg, .png" />
</div>
<button type="submit">Submit</button>
</form>
</div>
);
}
}
export default App;
I am stumped. If someone sees where I'm going wrong or if there is a gotcha with react that I don't know about I am all ears!
Update
I should have left this information before, my apologies. The project is bootstrapped with create-react-app. I get this problem in development as well as when serving the build in production.
create-react-app#1.3.3
react-scripts#1.0.10
node#8.2.1
yarn#0.27.5
macOS#10.12.6
chrome#59.0.3071.115

You are missing the brackets around event while catching the event onSubmit. Change it to below code and try:
<form onSubmit= {(e) => this.capture(e)}>

I was still in an electron mindset and did not ever think that MDN was telling me the truth that the browser does not give me access to the hard path of a file on the system for security reasons. This code box that was sent to me in Reactiflux shows a method for obtaining the end goal I was looking for, many thanks to #BTM for this:
https://codesandbox.io/s/lNN5pK6M

Related

How can I get a data from one html form and display it on another?

I'm using only JS and I want to get a username from one html and display it on the another html page. I'm a beginner on the programming and I have two questions:
Can I use only one JS file to do this? If yes, how?
Why it isn't working?
First page
<main>
<form action="">
<input type="text" name="login" id="login" class="login" placeholder="Login">
<a href="password.html" id='logar'>LOGAR!</a>
</form>
<span id='spanlogin'></span>
</main>
<script src="main.js"></script>
var login = document.getElementById('login').value;
localStorage.setItem("thelogin", login);
Second Page
<main>
<div>
<span id='showlogin'></span>
</div>
<div class="senha">
</div>
</main>
<script src="second.js"></script>
var ologin = localStorage.getItem('thelogin');
function showthelogin(){
document.getElementById('showlogin').innerHTML = ologin
}
window.onload = showthelogin()
Thanks for the help!
For what it's worth, the login button is just a link, it's not running the js on the first page. The js executes immediately on page load when the form is still empty. If you wrap it in a function and call the function on click, it will do what you want.
What you want, might not be what you need, but getting things to do what we want them to do can help us better understand what we need.
main.js
function savePassword(){
var login = document.getElementById('login').value;
localStorage.setItem("thelogin", login);
}
first page:
LOGAR!
In answer to your first question, you can do it with just one page. For example, read about Single-page applications.
Note that the way you're doing it is not the same thing as 'submitting' the form.
"For documents loaded from file: URLs (that is, files opened in the browser directly from the user’s local filesystem, rather than being served from a web server) the requirements for localStorage behavior are undefined and may vary among different browsers.
In all current browsers, localStorage seems to return a different object for each file: URL. In other words, each file: URL seems to have its own unique local-storage area. But there are no guarantees about that behavior, so you shouldn’t rely on it because, as mentioned above, the requirements for file: URLs remains undefined."
Its from developer.mozilla.org/en-US/docs/Web/API/Window/localStorage

React: How can I show an already existing image in react update form and then show the new one once a new image is uploaded?

hey guys i am learning react js and I have an update form to update book info. I am using django rest api for endpoints. I have a working form where I can upload files and do all those stuffs but I am not able to show the image which is already there in the template, Here I have a book cover image, which is already there in the database, it should be showing in the front-end and when I change the image, the new one should show, how can I add that feature here, I tried <img src={formData.book_cover} and consoling out this is showing the url, but the image isn't getting displayed.
From the network tab,
The problem I think is
Request URL:http://localhost:3000/media/book/book_sample/pride_in_nat.png
request url since the image gets displayed if the url is localhost:8000 instead of localhost:3000 as it is where the django server backend runs. So, how can I change that?
This is the code.
import React from "react";
function BookInfoForm() {
const initialFormData = Object.freeze({
id: '',
book_cover: '',
book_name: '',
book_summary: '',
});
const [formData, updateFormData] = useState(initialFormData);
const [image, setImage] = useState(null);
const { register, handleSubmit, control, errors } = useForm();
useEffect(() => {
axiosInstance.get('api/books/info/update/').then((res) => {
updateFormData({
...formData,
['book_cover']: res.data.book_cover,
['book_name']: res.data.book_name,
['book_summary']: res.data.book_summary,
});
});
}, [updateFormData]);
const handleChange = (e) => {
if (e.target.name === 'image') {
setImage({
image: e.target.files,
});
// console.log(e.target.files);
}
updateFormData({
...formData,
// Trimming any whitespace
[e.target.name]: e.target.value
});
};
const onSubmit = (data) =>{
let formData = new FormData();
formData.append('user', user.id),
formData.append('book_cover', data.image[0]),
formData.append('book_name', data.book_name),
formData.append('book_summary', data.book_summary),
axiosInstance.put('api/books/info/update/', formData),
}
return (
<>
<form className={classes.form} noValidate onSubmit={handleSubmit(onSubmit)}>
<Grid container spacing={2}>
<Grid item xs={6}>
{/* Show existing book cover and change when new one is uploaded */}
<img src={formData.store_logo} alt="" />
<label htmlFor="book-cover">
<input
accept="image/*"
className={classes.input}
id="book-cover"
onChange={handleChange}
name="image"
type="file"
ref={register}
/>
Book Cover
<IconButton color="primary" component="span">
<PhotoCamera />
</IconButton>
</label>
</Grid>
<Grid item xs={12}>
<TextField
variant="outlined"
required
fullWidth
id="book_name"
label="Book Name"
name="book_name"
autoComplete="book_name"
value={formData.book_name}
onChange={handleChange}
inputRef={register({maxLength: 30})}
rows={1}
/>
</Grid>
<Grid item xs={12}>
<TextField
variant="outlined"
required
fullWidth
id="book_summary"
label="Book Summary"
name="book_summary"
autoComplete="book_summary"
value={formData.book_summary}
onChange={handleChange}
inputRef={register({maxLength: 1000})}
multiline
rows={3}
/>
</Grid>
</Grid>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
>
Update
</Button>
</form>
</>
)
}
You might want to take a look at one of my answers on Why React needs webpack-dev-server to run?
As your frontend is running at localhost:3000 and you are providing a relative path to the img tag, the browser is assuming that the image is at localhost:3000.
Whenever your backend host is different than the frontend host, you have to provide a complete URL to the resource i.e., origin(http://localhost:8000) + path to the resource(/book/book_sample/pride_in_nat.png)
As you are storing the path to the resource in your database, just append the origin while giving it to the img tag.
<img src={`http://localhost:8000/${formData.store_logo}`} />
Suggestion
A better approach is to use .env files and load them according to your development or production environment
<img src={`${process.env.IMAGE_STORE_ORIGIN}${formData.store_logo}`} />
And in your .env file or .env.development file, you can add the entry for where your images are stored
In your .env file:
IMAGE_STORE_ORIGIN=http://localhost:8000/
So, when you want to change your backend server origin, you can just change it in one location and it is used inside your entire app instead of changing it manually every time you want to use a new server address.
Take a look at dotenv and dotenv-expand
I hope this should clarify your "why" and "what".
I would advise this as being the best solution and I highly would recommend this even though I have not worked heavily with Django.
I know with Django you can store files in media files as configured in your settings.py, as good and convenient that is I guess but the best solution in my understanding for managing files is dealing with a 3rd party like cloudinary(Very common when working with Node using 3rd party software's to manage files)
So the flow behind:
Make an account on cloudinary(Do not worry it's completely free but can upgrade)
When you want to save a file to DB you first interact with cloudinary with Django.
Once Django is done cloudinary Api will bring back a url of your image.
Then what you only have to do now is now save that link given in your database
Now with your Django Model
In the place where you had image as a "FileField" now you can safely convert to "CharField" or whatever that means text/string in django kind of forgot
Then now in your React app that should work
Cloudinary SDK docs for Django
https://cloudinary.com/documentation/django_integration
You can also look up some example on YouTube for clarity if docs are not clear
https://youtu.be/1T6G7Znrbfg

nuxt build Breaks VueJS Events

I'm running a nuxtjs application in production and it's causing some VueJS functionality to break, particular with DOM events. However, development mode works just fine as it should flawlessly, though it is slower than production mode due to the code not being minified and compiled and all that.
#click events do not fire their functionality
.prevent does not prevent anything
Here's my source of an example section that does not work.
The #click event calls that change the view do not do anything.
Upon hitting enter to fire v-on:keydown.enter="login" it does not get prevented and the form gets submitted as a GET request to the same page ( the URL shows the GET ?variables )
After looking at the HTML code in the browser to see if there's any logged warnings or errors, there's nothing and nothing on the server side logs.
Plus, the <button> tags that have the #click to fire the login or signup methods do not have any events on them, basically not doing anything; just HTML.
On my production server after running nuxt build by executing npm run build, there are no errors or warnings.
<template>
<div class='card'>
<div class='tabs 2-col'>
<span :class="{active : view != 'signup'}" #click="view = 'login'">
Login
</span>
<span :class="{active : view == 'signup'}" #click="view = 'signup'">
Sign Up
</span>
</div>
<div class='card-body'>
<form v-show="view == 'login'" v-on:keydown.enter="login" novalidate>
<!-- my other html -->
<button #click.prevent="login">Login</button>
</form>
<form v-show="view == 'signup'" v-on:keydown.enter="signup" novalidate>
<!-- my other html -->
<button #click.prevent="signup">Sign Up</button>
</form>
</div>
</div>
</template>
<script>
export default {
data : function(){
return { view : 'login'};
},
methods : {
login: function(){
// my functionality
},
signup:function(){
// my functionality
},
}
}
</script>
Thank you for any help! I've been banging my head for hours.
data proprerty should be a function that return the object. try to do this:
data: function () {
return {
view: 'login',
}
}

How to display server errors in Angularjs with ng-messages

I have have my angular app validating a sign-up form. On submit, the server also validates the data. I'm outputting error messages in angular using ng-messages.
Here is a shortened version of my form, which works perfectly so far.
<form name="signUpForm" novalidate data-ng-submit="attemptSignUp()">
<label for="firstName">First name</label>
<input type="email" name="email" id="email" required data-ng-model="data.user.email" />
<div class="error" data-ng-messages="signUpForm.email.$error" data-ng-show="signUpForm.$submitted" data-ng-cloak>
<p data-ng-message="required">Please provide your email</p>
</div>
</form>
The server verifies the email address is unique, and if not, returns a 422 error (from Laravel 5), with an array of errors.
[
'email' => 'This email is already in use'
]
I'd like to merge in this, and any other messages sent back from the server into their relevant ng-messages block. Any idea how I could accomplish it?
A simple solution is to have two arrays. One for client side and one for server side errors which is populated in your controller. You can hide the server side errors if client errors exists or opposit to avoid double messages.
The reason I choose to have two arrays instead of populating the forms array is that the JavaScript controller should not know or be dependent on the structure of the HTML. The HTML AngularJS template should be bound to the controller, not opposit.
<form name="signUpForm" novalidate data-ng-submit="attemptSignUp()">
<label for="email">E-mail
<input type="email" name="email" id="email" required data-ng-model="data.user.email" />
<div class="error" data-ng-messages="signUpForm.email.$error" data-ng-show="signUpForm.$submitted" data-ng-cloak>
<p data-ng-message="required">Please provide your email</p>
</div>
<div class="error" data-ng-messages="serverErrors" data-ng-show="signUpForm.$submitted" data-ng-cloak>
<p data-ng-message="emailexists">Email already exists</p>
</div>
</label
</form>
A note on the label: Users using screen-readers will not get your error messages read out loud to them if they are not wrapped inside the label.
Well, this is not the most elegant solution since you really should
leverage the asyncValidators in angular 1.3.x and then create your
custom validation directives.
Resources
http://plnkr.co/edit/s4jJAOqehBkFUC9osMsy?p=preview found in the post by this guy.
Possibly here http://odetocode.com/blogs/scott/archive/2014/10/16/working-with-validators-and-messages-in-angularjs.aspx
And of course in the docs
But be cautious as this is not in any way a complete example ready to be used. It's mostly here for demo purpose and to sort of give you an idea where to start. I have not bothered with clearing any previous errors, revalidating the form or taken into account other validation errors.
Awesomeness
Imagine your controller looks like this
$scope.serverValidations = {};
$scope.attemptSignUp = function(){
Api.validateEmail($scope.email).then(angular.noop, function(data){
$scope.serverValidations = data
for(prop in $scope.serverValidations){
if($scope.signUpForm[prop]){
angular.forEach($scope.serverValidations[prop],function(validation){
$scope.signUpForm[prop].$setValidity(validation.type, false);
});
}
}
});
}
and your response data containing validation errors look like this
{
email:[
{type:'unique', message:'This email is already in use'}
],
name:[
{type:'maxlength', message:'Your name is to long, get a new one :)'}
]
};
Then in your HTML you could do like this
<div class="error" data-ng-messages="signUpForm.name.$error" data-ng-cloak="">
<p data-ng-message="required">You don't have a name?</p>
<p ng-repeat="validation in serverValidations['name']" ng-message="{{validation.type}}">{{validation.message}}</p>
</div>
Here's a dirty Codepen for you: http://codepen.io/anon/pen/yyzMgG?editors=101
When you press submit, after 2 seconds (the time it takes to hit the fake server) your server validations are presented.
First of all you should set validity and error messages
$scope.formErrors = {};
angular.forEach(errors, function(data, name) {
if (!vm.register_form[name]) {
return;
}
$scope.formErrors[name] = data.message;
//this will set errors->server to invalid state
$scope.register_form[name].$setValidity('server', false);
});
The next step will be rendering by ng-messages
<div ng-messages="register_form.email.$error">
<div ng-message="required">Email is required</div>
<div ng-message="email">Invalid email</div>
<div ng-message="server">{{formErrors.email}}</div>
</div>
I had a similar question, but the solutions did not work for me. What I did, and it is/was a hack/work around, was to send different errorcodes, and set a case statement.

Simple form submit with files

I am trying to write a simple upload form with input file type using React JS. When i submit the file, it is not submitting multi form data. It is submitting plain input file name.
Do we need to make any other changes if we want to write upload functionality in React.
Basic javascript code for uploading (js fiddle link) :
/** #jsx React.DOM */
var HelloMessage = React.createClass({
render: function() {
return (<div>
<form name="secret" ENCTYPE="multipart/form-data" method="POST" action="http://localhost:8080/uploadFile">
Please choose two files to upload.
<br/>
File 1:<input type="file" id="profilePic" name="profilePic"/>
<br/>
<input type="submit" value="submit"/>
</form>
</div>);
}
});
React.renderComponent(<HelloMessage />, document.body);
Can someone help me to write using React JS.
Casing is important for React components. Try encType instead of ENCTYPE. (You should have seen a warning in your console suggesting that you use encType.)
Here's a complete list of tags and attributes:DOM Elements.

Categories

Resources