File upload inside window.addEventListener - forms

My brain's hurting. After my page loads, I get some HTML. This is a stripped-down version:
window.addEventListener('load', () => {
if (window.location.pathname === '/profile' && Cookies.get('token')) {
axios.get('/api/profile-info').then(res => {
const member = res.data.member
const memberInfo = `
<form enctype="multipart/form-data" id="uploadProfilePictureForm">
<input type="file"/>
<button onclick="uploadPicture(event)">Upload</button>
</form>
`;
})
}
})
I then handle the onclick event:
const uploadPicture = (event) => {
event.preventDefault()
const form = document.getElementById('uploadProfilePictureForm')
console.log(form) // Just shows the HTML form
}
This handler is placed before window.addEventListener
The file name appears on the page, but after clicking "Upload", it won't show in the console (which I plan to send to my server).
How do I allow an onclick event to handle a file upload?

Solved
Inside window.addEventListener(), I used a simple input tag:
<input type="file" id="fileUpload" onchange="uploadPicture()"/>
Then, outside this event listener, I defined the uploadPicture() function:
function uploadPicture() {
var FD = new FormData()
var fileInput = document.getElementById('fileUpload')
FD.append("pictureFile", fileInput.files[0])
const data = FD.entries().next().value
console.log('data\n', data) // This is the FormData array
}

Related

How to upload a single image file with next.js to mongodb?

When I'm uploading form data to mongodb, in the image section it shows this:
"C:\fakepath\imageName.jpg" with no actual image in it. And when I fetch the data from the database and map them, all the other data like title and body gets shown, but not the image. Because there wasn't any image to begin with. It was just a fake path.
So, how can I upload image file not the path to MongoDB.
I'm using Next.js and the mongodb npm package.
This is the pages/api/new-post.js file:
import { MongoClient } from "mongodb";
export default async function handler(req, res) {
if (req.method === 'POST') {
const data = req.body
const client = await MongoClient.connect('mongodb://localhost:27017/blog-nextjs')
const db = client.db()
const postCollections = db.collection('posts')
const result = await postCollections.insertOne(data)
console.log(result)
client.close()
res.status(201).json({message: 'Post Inserted'})
}
}
The form code I used in pages/new-post.js are:
import { useRef } from "react";
export default function NewPostForm(props) {
const titleInputRef = useRef()
const imageInputRef = useRef()
const bodyInputRef = useRef()
function submitHandler(e) {
e.preventDefault()
const enteredTitle = titleInputRef.current.value
const enteredImage = imageInputRef.current.value
const enteredBody = bodyInputRef.current.value
const postData = {
title: enteredTitle,
image: enteredImage,
body: enteredBody
}
props.onAddPost(postData)
}
return (
<div>
<form onSubmit={submitHandler}>
<div>
<label htmlFor="title">Title</label>
<input
placeholder="Post Title"
required
type="text"
ref={titleInputRef}
/>
</div>
<div>
<label htmlFor="body">Post</label>
<textarea
placeholder="Post Body "
required
ref={bodyInputRef}
></textarea>
</div>
<div>
<label htmlFor="image">Image</label>
<input
type="file"
required
placeholder="Image"
accept="image/png, image/gif, image/jpeg"
// accept="image/*"
ref={imageInputRef}
/>
</div>
<div><button>Post</button></div>
</form>
</div>
);
}
It's a bad approach to load images directly in MongoDb
You should store it to 3d party hostings and save only link(s) to your image.
Check related question

REACT Multiple Registration

I have a problem with React, so I created script and it doesn't work.
This should:
Render first state step (it's working) (Component First)
Here is error, it don't see default values.(name & email
After click Save And Continue it should save files to data.
And going to next steps in cases.
The error is
bundle.js:34147 Uncaught ReferenceError: email is not defined
function send(e){
e.preventDefault()
}
function nextStep(){
this.setState({
step:this.state.step + 1
})
}
function nextStep(){
this.setState({
step:this.state.step - 1
})
}
function saveAndContinue(e) {
e.preventDefault()
// Get values via this.refs
var data = {
name : this.refs.name.getDOMNode().value,
email : this.refs.email.getDOMNode().value,
}
this.props.saveValues(data)
this.props.nextStep()
};
var fieldValues = [
name : null,
email : null,
];
function saveValues(fields) {
return (
fieldValues = Object.assign({}, fieldValues, fields)
);
}
class Registration extends React.Component{
constructor () {
super()
this.state = {
step:1
}
}
render() {
switch (this.state.step) {
case 1:
return <First fieldValues={fieldValues}
nextStep={this.nextStep}
previousStep={this.previousStep}
saveValues={this.saveValues} />
case 2:
return <Two fieldValues={fieldValues}
nextStep={this.nextStep}
previousStep={this.previousStep}
saveValues={this.saveValues}/>
case 3:
return <Third fieldValues={fieldValues}
nextStep={this.nextStep}
previousStep={this.previousStep}
saveValues={this.saveValues}/>
case 4:
return <Success fieldValues={fieldValues} />
}
}
}
class First extends React.Component{
render(){
return(
<form onSubmit ={send}>
<div className="group">
<input className="text" type="text" ref="name" defaultValue={this.props.fieldValues.name}/>
<span className="highlight"></span>
<span className="bar"></span>
<label>Write Name</label>
</div>
<div className="group">
<input className="text" type="email" ref="email" defaultValue={this.props.fieldValues.email} />
<span className="highlight"></span>
<span className="bar"></span>
<label>Write Your Mail</label>
</div>
<button onClick={this.saveAndContinue}>Save and Continue</button>
</form>
)
}
}
There is no Two, Third and Success classes in your code, so I'm assuming they are similar to the First class.
A global function doesn't need this keyword. But in this case, you have to put saveAndContinue inside First class if it need to access the state.
In React, normally you don't have to set default value for input.
Link the input value to the state, and then setState in onChange event.
The string in placeholder is shown when the state is empty.
The code below shows how to work with input tag in React:
<input
value={this.state.inputValue}
onChange={e => {
this.setState({ inputValue: e.target.value });
}}
type="text"
placeholder="default value"
/>
Note that the state will updates onChange rather than click the save button.
Does this solve your problem?

Using Protractor: Switch to iframe using browser.switchTo().frame

So I have already written the testing script which:
1) Logs into the application framework, then
2) Clicks menu to launch the app which I am testing ("MyAwesomeApp.html" for this post)
And my main problem is: In navpanel-spec.js below, I want to target the https://server/apps/Default.aspx?r=1 URL, then click within the iframe where MyAwesomeApp is running.
**** ERROR Trying to switch to the iframe this way, but it does NOT work:
browser.switchTo().frame(element(by.id('1')).getWebElement());
Error in cmd prompt:
Started
[15:43:29] E/launcher - No element found using locator: By(css selector, *[id="\31 "])
...
sat-navpanel-spec.js:52:24)
So there are two URLs going on here:
1) https://server/apps/Default.aspx?r=1 (the main app framework with menu system in top nav).
2) https://server/apps/MyAwesomeApp.html (the web app which the test script launches within the <iframe> tag.
The html looks like this, where the application renders within the <iframe> :
<body>
<div id="top">
<!-- top nav menu systems rendered here -->
</div>
<div id="middle">
<div id="m1">
<div id="m2" class="hidden">
<div id="m3">
<div id="right" class="hidden">
<div>
<div id="frame_holder" style="height: 940px;">
<iframe style="width: 100%; height: 100%;" name="1" id="1" src="https://server/apps/MyAwesomeApp.html">
</iframe>
</div>
</div>
</div>
</div>
<div id="left" style="display: none;"></div>
</div>
</div>
</div>
</body>
In my Protractor.config.js file I have a few specs :
specs: [
'spec/login.js',
'spec/launch-awesome-app.js',
'spec/navpanel-spec.js',
'spec/another-spec.js',
'spec/yet-another-spec.js'
]
login.js and launch-awesome-app.js work fine. They log into the menu system, then click thru the menus in order to launch myAwesomeapp - no problem.
MY PROBLEM:
In navpanel-spec.js I want to target the https://server/apps/Default.aspx?r=1 URL, then click within the iframe where MyAwesomeApp is running.
However, it is NOT selecting any of my elements.
If I target https://server/apps/MyAwesomeApp.html in navpanel-spec.js, of course it launches a new browser window and runs the test just fine.
Here's my navpanel-spec.js test spec:
describe('Testing My Awesome App', function () {
var panelObj = new PanelObjects();
var urlDefault = 'https://server/apps/Default.aspx?r=1';
var urlApp = 'https://server/apps/MyAwesomeApp.html';
browser.get(urlApp); // Runs my AwesomeApp tests okay, HOWEVER it launches a new browser window.
browser.get(urlDefault); // Launches app framework with top nav menus and embedded <iframe>,
// HOWEVER I cannot select iframe and successfully run tests here.
beforeEach(function () {
browser.sleep(5000);
browser.waitForAngular();
});
// USE-CASE OBJECT !!
var items = browser.params.useCaseJsonFile["navigatePanels"];
browser.getAllWindowHandles().then(function (handles) {
handles.map(function (win, idx) {
browser.driver.getCurrentUrl().then(function (curr) {
if (curr.indexOf('Default.aspx') >= 0) {
browser.driver.switchTo().window(handles[idx]);
}
});
});
});
browser.switchTo().frame(element(by.id('1')).getWebElement());
var testId = element(by.id('middle'));
console.log(testId);
items.map(function (item) {
if (item.enableTest) {
var specItem = it(item.name, function () {
console.log('------------------------------');
console.log('---- ' + item.describe);
browser.waitForAngular();
// select panels, etc..
panelObj.panelClick(item.panelName).then(function () {
// ...
});
panelObj.getPanelText(item.panelName).then(function (title) {
expect(title).toContain(item.panelTitle);
});
});
}
});
});
UPDATE
var LoginObjects = require('../pageObjects/login-objects.js');
describe('Testing My Awesome App', function () {
var panelObj = new PanelObjects();
var loginObj = new LoginObjects();
//var urlDefault = 'https://server/apps/Default.aspx?r=1';
//browser.get(urlApp); // Runs my AwesomeApp tests okay, HOWEVER it launches a new browser window.
browser.ignoreSynchronization = true;
// LOGIN AND LAUNCH APP !!!
loginObj.Login();
loginObj.Launch();
beforeEach(function () {
browser.sleep(5000);
browser.waitForAngular();
});
// USE-CASE OBJECT !!
var items = browser.params.useCaseJsonFile["navigatePanels"];
// SWITCH TO iframe ELEMENT
loginObj.switchWindowAndFrame();
items.map(function (item) {
if (item.enableTest) {
var specItem = it(item.name, function () {
console.log('------------------------------');
console.log('---- ' + item.describe);
browser.waitForAngular();
// select panels, etc..
panelObj.panelClick(item.panelName).then(function () {
// ...
});
panelObj.getPanelText(item.panelName).then(function (title) {
expect(title).toContain(item.panelTitle);
});
});
}
});
});
and my page objects :
module.exports = function(){
this.Login = function(){
var url = browser.params.loginUrl;
browser.driver.get(url);
browser.sleep(200);
var userName = browser.params.credential.userId;
var password = browser.params.credential.password;
element(by.id('username')).clear().then(function(){
element(by.id('username')).sendKeys(userName);
element(by.id('password')).sendKeys(password);
});
browser.sleep(1000);
var that = this;
var submitElement = element(by.id('bthLogin'));
submitElement.click().then(function () {
browser.getAllWindowHandles().then(function (handles) {
// LOGIN MESSAGE WINDOW
browser.driver.getCurrentUrl().then(function(curr){
if (curr.indexOf('LoginMsg.aspx') >= 0){
// Do we really need to close the login successful browser ???
browser.driver.close();
}
});
browser.driver.switchTo().window(handles[1]);
});
});
},
this.Launch = function(){
var sel = '#TheMenu1 > ul > li:first-child';
var elem = element(by.css(sel));
elem.click().then(function(){
browser.sleep(1000);
var elem2 = element(by.cssContainingText('.rmLink', 'The First Menu Item'));
elem2.click();
// Select menu item; sleep before attempting to click().
var subElem = element(by.cssContainingText('.rmLink', 'My Awesome App'));
browser.sleep(1000);
subElem.click();
browser.waitForAngular();
});
},
this.switchWindowAndFrame = function(){
browser.getAllWindowHandles().then(function (handles) {
handles.map(function(win, idx){
browser.driver.getCurrentUrl().then(function(curr){
if (curr.indexOf('Default.aspx') >= 0){
browser.driver.switchTo().window(handles[idx]);
}
});
});
});
browser.switchTo().frame(element(by.css('[name="1"]')).getWebElement());
}
};
As mentioned in the comments above, protractor has a bug which prefixes '\3' to your id element with number.
The temporary way is to change you locator. :P

AngularJS ignores form submit

I'm using AngularJS v1.2.13 to create a page with a form which will download a user's file on click.
I'm using $sce to enable the injection of the file URL which works fine.
However, the loading of the resource disables the form submit. I'm sure it has to do with the resource load because when I remove the load and hardcode the url it works fine. I've also created a JSFiddle without it and have not been able to reproduce the problem there.
Any ideas on why this is happening and how it can be fixed?
HTML:
<div ng-controller="viewProfileController" data-ng-init="findOne();">
<form method="get" action="{{downloadFileURL}}">
<button type="submit" class="no-button comment-small" >
Download File
</button>
</form>
</div>
Controller:
'use strict';
angular.module('bop.viewProfile').controller('viewProfileController', [
'$scope', 'Users', '$sce', '$routeParams',
function($scope, Users, $sce, $routeParams) {
$scope.downloadFileURL = '';
// Find current user
$scope.findOne = function() {
Users.get({
userId: $routeParams.userId
}, function(user) {
$scope.user = user;
$scope.downloadFileURL = $sce.trustAsResourceUrl($scope.user.file.url);
});
};
}]);
Users Service:
var userServices = angular.module('bop.users', ['ngResource']);
userServices.factory('Users', ['$resource', function($resource) {
return $resource(
'users/:userId',
{ userId: '#_id' },
{ update: { method: 'PUT' } }
);
}]);

upload a file using angular, express and mongodb's gridfs

I am new to these technologies and hence have limited knowledge on how to upload a file. During my research, I have seen ngUpload and other javascript/directive based solutions. However, I am trying the following and not sure what else I am missing to complete it.
I am trying to upload file after creating a blog using angular-express-blog application. I have the following code
In view.jade
fieldset
h5 Add Media
form(name='theForm', enctype="multipart/form-data")
.clearfix
label Document Name
.input: input(ng-model='form.docName', name='docName', type='text')
.clearfix
label File
.input: input(ng-model='form.file', type="file", name="file")
.actions
button(ng-click="uploadFiles('/page3files')") Upload Files
the controller, I do need to return to the uploadfile page hence, I am passing in /page3files.
$scope.uploadFiles = function( path ) {
//alert("upload files clikced");
$http.post('/api/uploadFile', $scope.form).
success(function(data) {
$scope.form.docName='';
$scope.form.file='';
$location.path(path);
});
};
In the express routes file
exports.uploadFile = function (req, res) {
console.log("doc name: " + req.body.docName);
console.log("file name: " + req.body.file.name);
console.log("file path: " + req.body.file.path);
res.json(true);
};
Unfortunately, I am getting an exception at req.body.file.name saying cannot read property 'name' of undefined.
Any assistance is much appreciated.
Thanks,
Melroy
for the $http.post() to be able to upload files you need to set some configurations.
headers : {'Content-Type': undefined},
transformRequest: angular.identity
You can use the simple/lightweight angular-file-upload directive that takes care of that for you.
It supports drag&drop, file progress and file upload for non-HTML5 browsers with FileAPI flash shim.
<div ng-controller="MyCtrl">
<input type="file" ng-file-select="onFileSelect($files)" multiple>
</div>
JS:
//inject angular file upload directive.
angular.module('myApp', ['angularFileUpload']);
var MyCtrl = [ '$scope', '$upload', function($scope, $upload) {
$scope.onFileSelect = function($files) {
//$files: an array of files selected, each file has name, size, and type.
for (var i = 0; i < $files.length; i++) {
var $file = $files[i];
$upload.upload({
url: 'my/upload/url',
file: $file,
progress: function(e){}
}).then(function(data, status, headers, config) {
// file is uploaded successfully
console.log(data);
});
}
}
}];
There are more info on the README page and there is a Demo page
You can send file data with FormData object. For Example:
HTML:
<fieldset>
<legend>Upload Video</legend>
<input type="file" name="photo" id="photo">
<input type="button" ng-click="uploadVideo()" value="Upload">
</fieldset>
JS:
$scope.uploadVideo = function () {
var photo = document.getElementById("photo");
var file = photo.files[0];
var fd = new FormData(); //Create FormData object
fd.append('file', file);
$http.post('/uploadVideo', fd, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
}).success(function (data) {
// Do your work
});
};