XPage ajax document creation - javascript

This might seem like a duplicate but please read first and let me know if you have questions. I have a single xpage with two data sources, one form for information and the other for image attachments. Now unlike typical applications I do not want to attach the image to the existing main data source (the first one) I want to asynchronously upload images and create separate documents for each image with the main datasource's UNID as the subID of the new image document. This is the just the way of the current data structure and I have little to no say on changing this so here's the challenge is presented.
I have successfully been able to create xhr requests (with the guide of various async uploader previous works of Julian and Sven) and create new documents with images with the custom control I have written but once I embed it into the XPage with two datasources and attempt to upload and create a new document, it creates a duplicate of the main datasource and wipes out all the other field values (expected behavior of a duplicate with empty fields). I suspect it's because I use the $$submitid and all the required form values for my upload request and they're somehow tied or shared with the first datasource but I can't be too sure.
My custom control has it's own datasource and the xpage has a panel with a datasource as part of it. Custom Control below.
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.data>
<xp:dominoDocument var="imgAttach" formName="mImageAttach"
concurrencyMode="force">
</xp:dominoDocument>
</xp:this.data>
<xp:inputText id="inputText2" style="width:657.0px"><xp:this.defaultValue><![CDATA[#{javascript:facesContext.getExternalContext().getRequest().getRequestURI();
context.getUrl()}]]></xp:this.defaultValue></xp:inputText>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:inputText id="parentId" value="#{imgAttach.ParUNID}"
style="width:333.0px">
<xp:this.defaultValue><![CDATA[#{javascript:var url = context.getUrl().toString();
/*
if(url.indexOf("createComponent.xsp") > 0 && context.getUrlParameter("documentId") != ""){
var doc:NotesDocument = newComponent.getDocument();
return doc.getUniversalID();
} else {
return "";
}
*/}]]></xp:this.defaultValue>
<xp:this.rendered><![CDATA[#{javascript:var url = context.getUrl().toString();
if(url.indexOf("createComponent.xsp") > 0){
return true;
} else {
return false;
}}]]></xp:this.rendered>
</xp:inputText>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:inputText id="inputText1"
defaultValue="#{javascript:imgAttach.getDocument().getUniversalID()}"
style="width:333.0px; display:none;">
</xp:inputText>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:inputText id="Created" style="width:297.0px; display:none;"
value="#{imgAttach.Created}"
defaultValue="#{javascript:#Created()}">
<xp:this.converter>
<xp:convertDateTime type="date" dateStyle="short"></xp:convertDateTime>
</xp:this.converter>
</xp:inputText>
<xp:br></xp:br>
<xp:div styleClass="file-input">
<div class="file-preview ">
<div class="close fileinput-remove">×</div>
<div class="">
<div class="file-preview-thumbnails" id="file-thumbs">
</div>
<div class="clearfix"></div>
<div
class="file-preview-status text-center text-success">
</div>
<div class="fileinput-error file-error-message"
style="display: none;">
</div>
</div>
</div>
<div class="input-group-btn">
<button type="button" tabindex="500"
title="Clear selected files"
class="btn btn-default fileinput-remove fileinput-remove-button">
<i class="glyphicon glyphicon-trash"></i>
Remove
</button>
<button tabindex="500" id="upload-files"
title="Upload selected files"
class="btn btn-default fileinput-upload fileinput-upload-button">
<i class="glyphicon glyphicon-upload"></i>
Upload
</button>
<div tabindex="500" class="btn btn-primary btn-file"
id="files-container">
<i class="glyphicon glyphicon-folder-open"></i>
 Browse …
<input name="add-files[0]" id="add-files" type="file"
multiple="true" class="file-input">
</input>
<xp:fileUpload id="FileUploadCtrl"
value="#{imgAttach.Body}" useUploadname="true"
style="display:none">
</xp:fileUpload>
<xp:eventHandler event="onsaveDoc" submit="false"
refreshMode="norefresh" immediate="false" save="true" id="saveDoc" />
</div>
</div>
</xp:div>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:scriptBlock id="scriptBlock2" type="text/javascript">
<xp:this.value>
<![CDATA[
$(function() {
$("#files-container").delegate('input', "change", function() {
var files = !!this.files ? this.files : [];
if (!files.length || !window.FileReader){
//console.log("No file selected or no file reader suppport");
return; // no file selected, or no FileReader support
}
for(i=0; i<files.length; i++) {
if (/^image/.test( files[i].type)){ // only image file
var reader = new FileReader(); // instance of the FileReader
reader.readAsDataURL(files[i]); // read the local file
var img = document.createElement("img");
img.file = files[i];
img.name = 'no_'+ i;
img.classList.add("file-preview-image");
reader.onload = (function(aImg) { return function(e) { aImg.src = e.target.result; }; })(img);
$("#file-thumbs").append(img);
}
}
/*
//add new upload button
var currentInput = $('#add-files');
var nextInput = $('#files-container');
var inputsCount = $('.file-input').length;
// now we change the 'id' attribute of said element because id's should be unique
currentInput.attr('id','add-files'+inputsCount);
// and finally we hide the old element
currentInput.hide();
// now, we append a new input element with an incremented array key defined by the length of already existing input elements
nextInput.append('<input type="file" name="add-files['+inputsCount+']" id="add-files" multiple="true" class="file-input" />');
*/
});
$("#upload-files").on("click", function(e) {
e.preventDefault();
e.stopPropagation();
var all_files = [];
try {
var files = document.querySelectorAll(".file-preview-image");
for(var i = 0; i < files.length; i++) {
//
a_uploader(files[i].file, "#{id:saveDoc}", 0, 10);
}
} catch(e) {
console.log(e);
}
});
function a_uploader(file, uploadID, counter, maxSize){
try {
var formData = new FormData();
formData.append("#{id:FileUploadCtrl}", file);
formData.append("#{id:Created}", document.getElementById("#{id:Created}").value);
formData.append("$$viewid", dojo.query("input[name='$$viewid']")[0].value);
formData.append("$$xspsubmitid", uploadID);
formData.append( "view:_id1", "view:_id1");
if(document.getElementById("#{id:parentId}") != undefined ){
formData.append("#{id:parentId}", document.getElementById("#{id:parentId}").value);
}
var xhr = new XMLHttpRequest();
/* event listners */
xhr.upload.addEventListener("progress", function(e) {
if (e.lengthComputable) {
var percentComplete = Math.round(e.loaded * 100 / e.total);
console.log("Uploading... " +percentComplete.toString() + "%)");
}
else {
console.log("Uploading...")
}
}, false);
xhr.addEventListener("error", function(e) {
//insert error between error div
console.log("error: " + e);
}, false);
xhr.addEventListener("abort", function() {
//insert cancelled between div
console.log("Upload cancelled");
}, false);
xhr.open("PATCH", "#{javascript:context.getUrl()}");
xhr.send(formData);
} catch (e) {
console.log("a_uploader: "+e);
}
}
function clear_inputFiles(){
try {
var files = document.querySelectorAll(".file-preview-image");
for(var i = 0; i < files.length; i++) {
}
} catch(e) {
}
}
});]]>
</xp:this.value>
</xp:scriptBlock>
<xp:this.resources>
<xp:script src="/x$.js" clientSide="true"></xp:script>
</xp:this.resources>
</xp:view>

I thought I had set my first datasource to ignore request params as well as the data source inside of the component control. After setting the ignore request params to true on the first data source and finagling the datasource to get the document ID if it was edit mode, I was then able to use the custom control to upload images independently of the first/main datasource.
Now that they are uploading independently there is still a duplicate being created which seems odd to me. If I click the save button the first data source I don't create any image attachments but if I upload images it creates a duplicate of the primary doc.
Edit:
I had to remove the editDocument and documentId from the URL and specify my own docId as a parameter. I guess the HTTP referrer was the culprit in this case for creating the duplicate....or at least the way the page was set up from creating the doc to saving the doc.

Related

Uploading images to Cloudinary with REMIX (Web Fetch API)

I am creating a form with Remix, and I want upload images to Cloudinary.
I have here a Vanilla JS form example showing how to use the Web Fetch API to send images to Cloudinary: https://codepen.io/team/Cloudinary/pen/QgpyOK <— I would like to "transform" or "adapt" this code to work with Remix. Does anybody could help me, please?
I will paste the code I am working with so far.
On this code I've added already the HTML code from the CodePen link above. I just need to know how to exactly add the JS code, after the handleChange() I believe.
On the form element #fileElem we see there the onChange={handleFiles(this.files)} and this is what triggers the upload. My problem now, and because I am also already creating a post, is that I don't know exactly how/where to add all the handleFiles() code from the codepen link above.
On the code below you'll find comment blocks where I explain what I was did.
// import { isErrorResponse } from "#remix-run/react/data";
import { redirect, Form, useActionData, useTransition } from 'remix';
import { createPost } from '~/post';
export let action = async ({ request }) => {
let formData = await request.formData();
let title = formData.get('title');
let slug = formData.get('slug');
let markdown = formData.get('markdown');
let errors = {};
if (!title) errors.title = true;
if (!slug) errors.slug = true;
if (!markdown) errors.markdown = true;
if (Object.keys(errors).length) {
return errors;
}
await createPost({ title, slug, image, markdown });
return redirect('/admin');
};
export default function NewPost() {
let errors = useActionData();
let transition = useTransition();
let slug = '';
const handleChange = (e, document) => {
let text = e.target.value;
// using regex and replace, let's convert spaces to dashes
slug = text.replace(/\s/g, '-');
// lets set the value of the slug text box to be our new slug in lowercase
document.getElementById('slugInput').value = slug.toLowerCase();
};
// ATTEMPT - added bellow the JS code from codepen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// ATTEMPT - added bellow the JS code from codepen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// ATTEMPT - added bellow the JS code from codepen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
const cloudName = 'demo';
const unsignedUploadPreset = 'doc_codepen_example';
var fileSelect = document.getElementById('fileSelect'),
fileElem = document.getElementById('fileElem'),
urlSelect = document.getElementById('urlSelect');
fileSelect.addEventListener(
'click',
function (e) {
if (fileElem) {
fileElem.click();
}
e.preventDefault(); // prevent navigation to "#"
},
false
);
urlSelect.addEventListener(
'click',
function (e) {
uploadFile('https://res.cloudinary.com/demo/image/upload/sample.jpg');
e.preventDefault(); // prevent navigation to "#"
},
false
);
// ************************ Drag and drop ***************** //
function dragenter(e) {
e.stopPropagation();
e.preventDefault();
}
function dragover(e) {
e.stopPropagation();
e.preventDefault();
}
dropbox = document.getElementById('dropbox');
dropbox.addEventListener('dragenter', dragenter, false);
dropbox.addEventListener('dragover', dragover, false);
dropbox.addEventListener('drop', drop, false);
function drop(e) {
e.stopPropagation();
e.preventDefault();
var dt = e.dataTransfer;
var files = dt.files;
handleFiles(files);
}
// *********** Upload file to Cloudinary ******************** //
function uploadFile(file) {
var url = `https://api.cloudinary.com/v1_1/${cloudName}/upload`;
var xhr = new XMLHttpRequest();
var fd = new FormData();
xhr.open('POST', url, true);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
// Reset the upload progress bar
document.getElementById('progress').style.width = 0;
// Update progress (can be used to show progress indicator)
xhr.upload.addEventListener('progress', function (e) {
var progress = Math.round((e.loaded * 100.0) / e.total);
document.getElementById('progress').style.width = progress + '%';
console.log(`fileuploadprogress data.loaded: ${e.loaded}, data.total: ${e.total}`);
});
xhr.onreadystatechange = function (e) {
if (xhr.readyState == 4 && xhr.status == 200) {
// File uploaded successfully
var response = JSON.parse(xhr.responseText);
// https://res.cloudinary.com/cloudName/image/upload/v1483481128/public_id.jpg
var url = response.secure_url;
// Create a thumbnail of the uploaded image, with 150px width
var tokens = url.split('/');
tokens.splice(-2, 0, 'w_150,c_scale');
var img = new Image(); // HTML5 Constructor
img.src = tokens.join('/');
img.alt = response.public_id;
document.getElementById('gallery').appendChild(img);
}
};
fd.append('upload_preset', unsignedUploadPreset);
fd.append('tags', 'browser_upload'); // Optional - add tag for image admin in Cloudinary
fd.append('file', file);
xhr.send(fd);
}
// *********** Handle selected files ******************** //
var handleFiles = function (files) {
for (var i = 0; i < files.length; i++) {
uploadFile(files[i]); // call the function to upload the file
}
};
// ATTEMPT - end of JS code from codepen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// ATTEMPT - end of JS code from codepen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// ATTEMPT - end of JS code from codepen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// ATTEMPT - end of JS code from codepen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// ATTEMPT - end of JS code from codepen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
return (
<Form method='post'>
<p>
<label htmlFor=''>
Post Title: {errors?.title && <em>Title is required</em>}{' '}
<input onChange={handleChange} type='text' name='title' />
</label>
</p>
<p>
<label htmlFor=''>
{' '}
Post Slug: {errors?.slug && <em>Slug is required</em>}
<input placeholder={slug} id='slugInput' type='text' name='slug' />
</label>
</p>
<p>
<label htmlFor='markdown'>Markdown:</label>{' '}
{errors?.markdown && <em>Markdown is required</em>}
<br />
<textarea name='markdown' id='' rows={20} cols={30} />
</p>
<p>
<button type='submit'>
{transition.submission ? 'Creating...' : 'Create Post'}
</button>
</p>
{/* // ATTEMPT - added HTML form code from codepen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */}
{/* // ATTEMPT - added HTML form code from codepen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */}
{/* // ATTEMPT - added HTML form code from codepen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */}
<div id='dropbox'>
<h1>Client-Side Upload to Cloudinary with JavaScript</h1> Learn more in
this blog post -{' '}
<a href='https://cloudinary.com/blog/direct_upload_made_easy_from_browser_or_mobile_app_to_the_cloud'>
Direct upload made easy from browser or mobile app to the cloud
</a>
<div className='form_line'>
<h4>
Upload multiple files by clicking the link below or by dragging and
dropping images onto the dashed region
</h4>
<div className='form_controls'>
<div className='upload_button_holder'>
<input
type='file'
id='fileElem'
multiple
accept='image/*'
style={{ display: 'none' }}
onChange={handleFiles(this.files)}
/>
<a href='#' id='fileSelect'>
Select some files
</a>
<a href='#' id='urlSelect'>
URL Upload
</a>
</div>
</div>
</div>
<div className='progress-bar' id='progress-bar'>
<div className='progress' id='progress'></div>
</div>
<div id='gallery' />
</div>
{/* // end of ATTEMPT */}
{/* // end of ATTEMPT */}
{/* // end of ATTEMPT */}
</Form>
);
}
I've also found this post about this topic, but the code seems to be gone forever from the internet: https://twitter.com/remix_run/status/1494096161669464064?lang=en
Thank you!
The remix example was moved here

Cloned, file upload and preview issue with Jquery

I have a clone creation command. The id parameter created as clone is as follows (example: id = "myid_1"), increments it by one and makes id = "myid_2". So far there is no problem. That way, each object has a unique ID value, but a simple file upload and preview function within these clones causes my function to malfunction.
I created a simple example on jsfiddle. https://jsfiddle.net/magecode/mbk9ps2x/12/
The problem I understand here is that the id value of the file upload in the onhange event must increase in parallel with the image preview id. For this, I resorted to the attr function and tried to increase the id in parallel, but it never changed.
The code I want to do in the example. The value i is always incrementing but is not added to the cloned object.
<div class="clonable-block" data-toggle="cloner">
<a href="#" id="addrow" class="btn btn-info btn-block clonable-button-add" style="border-radius: 0px; font-size: 13px;">
<i class="fa fa-file pr-2"></i>Add Row</a>
<div class="clonable">
<br/>
<br/>
<img id="upload_1" alt="your image" width="100" height="100" class="clonable-increment-id" />
<input type="file" id="preview_1"
onchange="document.getElementById('upload_1').src = window.URL.createObjectURL(this.files[0])">
<br/>
<br/>
</div>
</div>
var i = 1;
$("#addrow").click(function () {
i++;
$("#upload_" + i+"").attr("onchange", "document.getElementById('preview_'+ i + '').src = window.URL.createObjectURL(this.files[0])");
});
I recreated this with vanilla.js, you can then transform it to Jquery.
The thing is that it could be much easier. I recommend you vanilla.js, it has a better overall performance, but i understand the simplicity of Jquery.
Here it goes:
const CloneSource = document.querySelector(".clonable-source");
const buttonRow = document.getElementById("add_row");
const clonesContainer = document.getElementById("clones");
let globalCounter = 1; // Create a globalCounter for the Form IDs
window.addEventListener("change", function(e) {
let target = e.target
// This try catch is just for strictly saying that we
// want to target the .clonable class
try {
let form = target.closest(".clonable")
let fd = new FormData(form)
let img = fd.get("img");
let preview = form.children[0]
let url = URL.createObjectURL(img)
preview.setAttribute("src", url);
} catch {
console.log("No Form")
}
})
buttonRow.addEventListener("click", function(e) {
// Creates Form, img, and input elements
let formSource = document.getElementById("source")
let Form = document.createElement("form")
let Img = document.createElement("img")
let Input = document.createElement("input")
Form.className = `clonable clonable-clone`
Form.setAttribute("id", `clonable-${globalCounter}`) // This add the ID to the Form
globalCounter++; // Sum 1 to the globalCounter;
Img.setAttribute("src", formSource.children[0].getAttribute("src"))
Input.setAttribute("type", "file")
Input.setAttribute("name", "img")
Form.appendChild(Img)
Form.appendChild(Input)
// Then append the form to the clones container
clonesContainer.appendChild(Form);
});
img {
width: 5rem;
}
<button id="add_row">Add Row</button>
<div id="clones">
<form class="clonable clonable-source" id="source">
<img src="#">
<input type="file" name="img">
<p>Select an Image</p>
</form>
</div>

Is there a way to "onload" open a TXT file in textarea and automatically save it on close or URL change?

Is there a way to "onload" open a TXT file in textarea and automatically save it on close or URL change?
I need this to auto open a TXT file and auto save it when exiting the page or when closing the browser.
JavaScript:
/** ReLoad File BEGINS**/
function ReLoadFile()
{
var FileToLoad = document.getElementById("FileToLoad").files[0];
var fileReader = new FileReader();
fileReader.onload = function(fileLoadedEvent)
{
var textFromFileLoaded = fileLoadedEvent.target.result;
document.getElementById("text").value = textFromFileLoaded;
};
fileReader.readAsText(FileToLoad, "UTF-8");
}
/** ReLoad File ENDS**/
/** Save File As FUNC p-1 BEGINS **/
var types = [
{"extension": ".html", "name": "HTML"},
{"extension": ".txt", "name": "Plain Text"},
{"extension": ".js", "name": "Javascript"},
{"extension": ".css", "name": "CSS"},
]
types.forEach(function(type) {
$opt = $("<option>").attr("value", type.extension).text(type.name)
$("#saveas").append($opt)
})
/** Save return if empty BEGINS**/
function SaveAsType()
{
if (document.getElementById("FileNameToSaveAs").value == "") {
alert("``Filename Save As`` name is empty.\n Please give the file a name that you will save it as, before you save it.");
return false;
} else {
/** Save File As FUNC p-2 BEGINS **/
{
console.log($("#saveas").val())
{
var textToSave = document.getElementById("text").value;
var textToSaveAsBlob = new Blob([textToSave], {type:"text/plain" });
var textToSaveAsURL = window.URL.createObjectURL(textToSaveAsBlob);
var fileNameToSaveAs = document.getElementById("FileNameToSaveAs").value + "" + $("#saveas").val();
var downloadLink = document.createElement("a");
downloadLink.download = fileNameToSaveAs;
downloadLink.innerHTML = "Download File";
downloadLink.href = textToSaveAsURL;
downloadLink.onclick = destroyClickedElement;
downloadLink.style.display = "none";
document.body.appendChild(downloadLink);
downloadLink.click();
}
}
/** Save File As FUNC p-2 ENDS **/
}
}
/** Save return if empty ENDS**/
/** Save File As FUNC p-1 ENDS **/
function destroyClickedElement(event)
{
document.body.removeChild(event.target);
}
/** Save File As ENDS **/
HTML:
<input type="file" id="FileToLoad" name="fileLoadName">
<input type="button" onclick="ReLoadFile();" value=" Re-Load ">
<textarea name="text" id="text" rows="34" cols="134" wrap="soft" placeholder="STEP - 1 : Put or load your web page Source Codes here"></textarea>
<textarea id="FileNameToSaveAs" rows="1" cols="30" maxlength="40" placeholder=" Filename Save As "></textarea>
<input type="button" onClick="SaveAsType();" value=" Save ">
using jquery you can do the auto open very easily:
$(document).ready(function() {
// do what you want with the text file
});
For dealing with when the browser closes you might be able to make use of jquery's unload() event. See here for documentation: https://api.jquery.com/unload/
Note that the unload event gets triggered when the user moves away from the page. Therefore, the back and forward buttons, as well as clicking on a link will trigger this event in addition to it being triggered when the browser is closed.

How to prevent uploaded image from getting deleted when editing a form?

I'm working on an app that allows users to post events. I'm using NodeJS, Express and Mongo.
I created a form that allows users to input event details, and upload an image relating to the event. I also created a form that allows the user to edit event details.
The form looks as follows:
The Problem:
User fills form with event details and attaches a picture.
User submits form
User decides he wants to change the event title, but NOTHING ELSE
User clicks edit event, changes the title, and submits
The problem: Even though the user didn't delete the picture associated with the event, the picture is no longer there.
Here is part of my new.ejs file (for posting new event, just adding this here for reference)
<script type="text/javascript" src="/js/eventform.js"></script>
<form action="/events"
method="POST"
enctype="multipart/form-data"
onSubmit="return(validate(this));" // validating user input
novalidate >
....
....
<input name="image" type="file" id="image" accept="image/*" style="display:none" onchange="handleFiles(this.files)">
<div id="imageBorder" >
<div id="imageContainer">
<div id="dropbox">
<i class="fa fa-picture-o" aria-hidden="true"></i>
<p> Drop image here or click to upload</p>
</div>
<div id="preview" class="hidden">
</div>
<button id="fileSelect" class="...">Upload Image</button>
<button id="fileRemove" class="...">Remove Image</button>
</div>
</div>
....
....
</form>
Notice that I'm using a hidden input field. Also I have two divs, preview (hidden initially) and dropbox. When an image is uploaded, the class 'hidden' is removed from preview and added to dropbox.
Here is part of the js file newevent.js
$(document).ready(function() {
....
eventImageSetup();
....
}
....
function eventImageSetup() {
var dropbox = document.getElementById("dropbox"),
fileElem = document.getElementById("image"),
fileSelect = document.getElementById("fileSelect"),
fileRemove = document.getElementById("fileRemove");
$(dropbox).height($('#imageBorder').height());
fileSelect.addEventListener("click", function(e) {
if (fileElem) {
fileElem.click();
e.preventDefault(); // to prevent submit
}
}, false);
fileRemove.addEventListener("click", function(e) {
e.preventDefault(); // prevent submit
if(!$('#preview').hasClass('hidden')) { // if there is an image
$('#preview').empty();
$('#dropbox').removeClass('hidden');
$('#preview').addClass('hidden');
$('#fileSelect').text('Upload Image');
$('#image').wrap('<form>').closest('form').get(0).reset();
$('#image').unwrap();
}
removeError($('#imageError'), $('#image'));
});
dropbox.addEventListener("dragenter", dragenter, false);
dropbox.addEventListener("dragover", dragover, false);
dropbox.addEventListener("drop", drop, false);
}
function handleFiles(files) {
var file = files[0];
.... // some error checking
var img = document.createElement("img");
img.id = "uploadedImage";
img.file = file;
img.onload = function() {
adjustImageSize(img);
};
$('#dropbox').addClass('hidden');
$('#preview').removeClass('hidden');
$('#preview').empty();
$('#preview').append(img);
$('#fileSelect').text('Replace Image');
var reader = new FileReader();
reader.onload = (function(aImg) {
return function(e) {
aImg.src = e.target.result;
};
})(img);
reader.readAsDataURL(file);
}
Here is part of my edit.ejs file
<form action="/events/<%=event._id%>?_method=PUT"
method="POST"
enctype="multipart/form-data"
onSubmit="return(validate(this));"
novalidate >
<input name="image" type="file" id="image" accept="image/*" style="display:none" onchange="handleFiles(this.files)">
<div id="imageBorder" >
<div id="imageContainer">
<div id="dropbox" class="hidden">
<i class="fa fa-picture-o" aria-hidden="true"></i>
<p> Drop image here or click to upload</p>
</div>
<div id="preview">
<script>
var imageExists = '<%=event.image%>';
if(imageExists) {
var myImg = document.createElement("IMG");
var source = "../../<%= event.image %>";
myImg.src = source;
adjustImageSize(myImg);
$('#preview').append(myImg);
}
</script>
</div>
<button id="fileSelect" class="...">Upload Image</button>
<button id="fileRemove" class="...">Remove Image</button>
</div>
</div> <!-- END OF imageBorder -->
....
</form>
The script above succesfully makes the image appear in the edit page, as follows.
But when you click submit, the picture doesn't show up.
Here is the nodejs route file. You can see the problem here
// UPDATE SPECIFIC EVENT IN DATABASE
router.put("/:id", upload.single('image'), middleware.checkEventOwnership, function(req, res) {
var filepath = undefined;
if(req.file) {
filepath = req.file.path.substr(7); // Substr to remove "/public"
}
req.body.image = filepath;
Event.findByIdAndUpdate(req.params.id, req.body, function(err, foundEvent) {
if(err) {
console.log(err);
req.flash("error", err);
} else {
req.flash("success", "Successfully edited your event");
}
res.redirect("/events/" + req.params.id);
});
});
Basically, if I leave the image untouched in the edit form, req.file doesn't exist. Thus, req.body.image = undefined. And an image is no longer associated with the event.
Common sense would say do this
if(req.file) {
filepath = req.file.path.substr(7);
req.body.image = filepath;
}
But if you do that, you introduce a new problem: If the user edits the event and removes the image (i.e decides he doesn't want an image associated with the event), the image never gets deleted.
Any idea how to solve this problem? I know I have to do something in the edit.ejs script... More specifically, I need to create an image file... But I'm not sure how to approach this
So I got this to work through a hack I REALLY don't like. I'm sure there is a better, cleaner, standard way of dealing with edit.ejs and images. In other words, please help me find a better solution!
Here are the changes in edit.ejs
<form action="/events/<%=event._id%>?_method=PUT"
method="POST"
enctype="multipart/form-data"
onSubmit="return validate(this) & editPageImageProcessing(this);"
novalidate >
....
....
<div id="preview">
<script>
var imageExists = '<%=event.image%>';
if(imageExists) {
var myImg = document.createElement("IMG");
var source = "../../<%= event.image %>";
myImg.src = source;
myImg.name = "previewImage";
myImg.id = "previewImage";
adjustImageSize(myImg);
$('#preview').append(myImg);
}
</script>
</div>
Basically, I added the lines
myImg.name = "previewImage";
myImg.id = "previewImage";
and added a function editPageImageProcessing.
What this function does is: IF the user didn't upload a new image, and did not delete the image, create a hidden input field "hiddenImage", and let its value be the source of the original image. See below:
// This function deals with edit image
function editPageImageProcessing(form) {
// If the user didn't change the image
// preview would be NOT hidden
// there would not be a req.file (document.getElementById('image').val == '')
// we want a hiddenimage input field
var aFile = document.getElementById('image').value;
console.log("File: ", aFile);
var preview = document.getElementById('preview');
console.log("Preview has hidden class: " + $(preview).hasClass('hidden'));
if(aFile == '' && !$(preview).hasClass('hidden')) {
var input = document.createElement('input');
$(input).attr("name", "hiddenImage");
$(input).attr("id", "hiddenImage");
$(input).attr("type", "hidden");
var myImage = document.getElementById('previewImage');
$(input).attr("value", myImage.src);
$('form').append(input);
}
return true;
}
Now, in the edit route, I did this
// UPDATE SPECIFIC EVENT IN DATABASE
router.put("/:id", upload.single('image'), middleware.checkEventOwnership, function(req, res) {
var filepath = undefined;
if(req.file) { // If user uploaded a new image
filepath = req.file.path.substr(7); // Substr to remove "/public"
console.log(filepath);
} else if(req.body.hiddenImage) { // If user kept the same image
var index = req.body.hiddenImage.lastIndexOf("/uploads");
filepath = req.body.hiddenImage.substr(index);
// req.body.hiddenImage WILL ONLY EXIST if user left image unchanged
}
req.body.image = filepath; // If user deleted image, this will be undefined, which is what we want
Event.findByIdAndUpdate(req.params.id, req.body, function(err, foundEvent) {
if(err) {
console.log(err);
req.flash("error", err);
} else {
req.flash("success", "Successfully edited your event");
}
res.redirect("/events/" + req.params.id);
});
});
Okay, so its messy.
Any better solution would be appreciated

Preview image HTML & JavaScript

I am working on an image uploader and I am currently trying to build up an image preview before the upload is done. I have this HTML code:
<input type="file" id="id_imatgeNewPeces"></input><br></br>
<img id="previewing" src="" />
Then I add a listener to the input like this:
document.getElementById('id_imatgeNewPeces').addEventListener('change', this.handleFileSelect, false);
And finally the function handleFileSelect:
handleFileSelect: function(oEvent) {
var file = oEvent.target.files[0];
if (!file.type.match('image.*')) {
sap.m.MessageToast.show("El archivo seleccionado no es una Imagen");
} else {
var readerURL = new FileReader();
readerURL.onload = function() {
$('#previewing').attr('src', readerURL.result);
$('#previewing').attr('width', '250px');
$('#previewing').attr('height', '230px');
return true;
};
readerURL.readAsDataURL(file);
}
},
I don't know why when I select a file which is not an image it displays the message but when I do select an image when the method onload ends no image is displayed and in addition it seems that the listener has been lost because no further calls are done when if I select another image.
The funny thing is that if I place a breakpoint on line '$('#previewing').attr('height', '230px');' I can see the image but when I continue it disappears. In addition when the method onload ends the fileinput "resets" I mean that it says that it has no selected files.
Besides being a old question, I've found that your code is working as expected:
I just don't undestand why use a native addEventListener() when you are using jquery for DOM manipulation, being easily replaced by:
$("#id_imatgeNewPeces").change(handleFileSelect);
var handleFileSelect = function(oEvent) {
var file = oEvent.target.files[0];
if (!file.type.match('image.*')) {
console.log("El archivo seleccionado no es una Imagen");
} else {
var readerURL = new FileReader();
readerURL.onload = function() {
$('#previewing').attr('src', readerURL.result);
$('#previewing').attr('width', '250px');
$('#previewing').attr('height', '230px');
return true;
};
readerURL.readAsDataURL(file);
}
};
$("#id_imatgeNewPeces").change(handleFileSelect);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="file" id="id_imatgeNewPeces">
<br/><br/>
<img id="previewing" src="" />

Categories

Resources