Adding form input fields dynamically - javascript

HTML FORM
<div class="module">
<div class="moduleTitle">Upload Photos</div>
<form id="upload" method="post" action="actions/upload.php" enctype="multipart/form-data">
<div id="drop">
Drop Here
<a>Browse</a>
<input type="file" name="upl" multiple />
</div>
<form onSubmit="addTags();return false;" id="tagAddForm">
<ul>
<!-- The file uploads will be shown here -->
</ul>
<input type="submit" name="login" value="Add Tags" class="submit" id="login"/>
</form>
</div>
</form>
Javascript File
$(function(){
var ul = $('#upload ul');
$('#drop a').click(function(){
// Simulate a click on the file input button
// to show the file browser dialog
$(this).parent().find('input').click();
});
// Initialize the jQuery File Upload plugin
$('#upload').fileupload({
// This element will accept file drag/drop uploading
dropZone: $('#drop'),
// This function is called when a file is added to the queue;
// either via the browse button, or via drag/drop:
add: function (e, data) {
var tpl = $('<li class="working uploaded"><input type="text" value="0" data-width="48" data-height="48"'+
' data-fgColor="#0788a5" data-readOnly="1" data-bgColor="#3e4043" /><p></p><span></span></li>');
// Append the file name and file size
tpl.find('p').text(data.files[0].name);
// Add the HTML to the UL element
data.context = tpl.appendTo(ul);
// Initialize the knob plugin
tpl.find('input').knob();
// Listen for clicks on the cancel icon
tpl.find('span').click(function(){
if(tpl.hasClass('working')){
jqXHR.abort();
}
tpl.fadeOut(function(){
tpl.remove();
});
});
// Automatically upload the file once it is added to the queue
var jqXHR = data.submit();
},
progress: function(e, data){
// Calculate the completion percentage of the upload
var progress = parseInt(data.loaded / data.total * 100, 10);
// Update the hidden input field and trigger a change
// so that the jQuery knob plugin knows to update the dial
data.context.find('input').val(progress).change();
if(progress == 100){
data.context.removeClass('working');
}
},
fail:function(e, data){
// Something has gone wrong!
data.context.addClass('error');
}
});
// Prevent the default action when a file is dropped on the window
$(document).on('drop dragover', function (e) {
e.preventDefault();
});
// Helper function that formats the file sizes
function formatFileSize(bytes) {
if (typeof bytes !== 'number') {
return '';
}
if (bytes >= 1000000000) {
return (bytes / 1000000000).toFixed(2) + ' GB';
}
if (bytes >= 1000000) {
return (bytes / 1000000).toFixed(2) + ' MB';
}
return (bytes / 1000).toFixed(2) + ' KB';
}
});

Here is a demo how you could dynamically generate (input/li)s based on an array of files.
//Create an empty container
var $elems = $();
//Cycle thru the files
$.each(data.files, function(idx, file) {
//Create an input with attrs
var $input = $("<input/>", {
'type': 'text',
'placeholder': 'separate tags with commas',
'name': file["name"]
});
//Create list element & append input
var $li = $("<li/>").append($input);
//Populate the container with the list item
$elems = $elems.add($li);
});
//Append all list items
$("#tagAddForm > UL").append($elems);
And a working demo with sample files that I defined for the demo.
Another one with option to display image thumbs based on the file name.

Related

Dropzonejs resort appended images

I have dropzone box and I've implemented sortable into it. As my form submits with html and not ajax I had to add hidden inputs where I push my selected images with dropzonejs therefore I could get them in back-end.
So far everything I explained above is working
Issue
As I mentioned I've implemented sortable functionality into dropzonejs and it does sort images in dropzone box, but not in my appended hidden input
In order to get sorted images in back-end I need to apply that sortable into my appended input as well.
Code
HTML
//Drop zone box
<div class="dropzone" id="my-awesome-dropzone" action="#">
<div class="fallback">
<input name="cimages[]" type="file" multiple />
</div>
<div class="clearfix"></div>
</div>
// append hidden input include selected images for back-end use
<div id="botofform"></div>
Script
Dropzone.autoDiscover = false;
var myDropzone = new Dropzone("#my-awesome-dropzone", {
headers: {
"X-CSRF-TOKEN": $("meta[name='csrf-token']").attr("content")
},
autoProcessQueue : false,
acceptedFiles: ".jpeg,.jpg,.png,.gif",
dictDefaultMessage: "Drag an image here to upload, or click to select one",
maxFiles: 15, // Maximum Number of Files
maxFilesize: 8, // MB
addRemoveLinks: true,
dictRemoveFile: 'Remove',
dictFileTooBig: 'Image is bigger than 8MB',
// append hidden input and add selected images
accept: function(file) {
let fileReader = new FileReader();
fileReader.readAsDataURL(file);
fileReader.onloadend = function() {
let content = fileReader.result;
$('#botofform').append('<input type="hidden" class="cimages" name="cimages[]" value="'+ content +'">');
file.previewElement.classList.add("dz-success");
}
file.previewElement.classList.add("dz-complete");
}
});
// reorder images in dropzone box (need to get this results into "$('#botofform').append" above)
$(function(){
$(".dropzone").sortable({
items:'.dz-preview',
cursor: 'move',
opacity: 0.5,
containment: '.dropzone',
distance: 20,
tolerance: 'pointer',
});
});
Question
How can I apply sortable results into my appended input?
How do I reduce $('#botofform') items (inputs) when an image gets removed by dropzonejs?
You can use data-attribute for both your div where image is added and input field .So, whenever new file is uploaded you can use setAttribute("data-id", count) here count can be any random number only make sure this value should be same for both input and div because this will help us to remove and sort inputs .
Now , for sorting inputs you can use stop event this will get called when sorting is stop .Inside this you can loop through dropzone div and then get attribute which we have added earlier using this attribute we can find the input and append same to botofform div.
Finally , for deleting files you can use remove files event this will get called whenever remove link is clicked so here you simply need to get that data-id which is added to div then use this attribute to remove input as well.
Demo Code :
var count;
var random;
Dropzone.autoDiscover = false;
var myDropzone = new Dropzone("#my-awesome-dropzone", {
headers: {
"X-CSRF-TOKEN": $("meta[name='csrf-token']").attr("content")
},
autoProcessQueue: false,
acceptedFiles: ".jpeg,.jpg,.png,.gif",
dictDefaultMessage: "Drag an image here to upload, or click to select one",
maxFiles: 15, // Maximum Number of Files
maxFilesize: 8, // MB
addRemoveLinks: true,
dictRemoveFile: 'Remove',
dictFileTooBig: 'Image is bigger than 8MB',
// append hidden input and add selected images
accept: function(file) {
let fileReader = new FileReader();
fileReader.readAsDataURL(file);
fileReader.onloadend = function() {
random = 1 + Math.floor(Math.random() * 1000);
count = $(".dz-complete").length + "_" + random;
let content = fileReader.result;
console.log(count)
//add dataid
$('#botofform').append('<input type="text" class="cimages" name="cimages[]" data-id = "' + count + '" value="' + content + '">');
file.previewElement.classList.add("dz-success");
file.previewElement.setAttribute("data-id", count);
}
file.previewElement.classList.add("dz-complete");
},
removedfile: function(file) {
console.log("inside remove --" + file.previewElement.getAttribute("data-id"))
var ids = file.previewElement.getAttribute("data-id") // get attr where file is been removed
$("#botofform input[data-id=" + ids + "]").remove() //remove input field as well
file.previewElement.remove(); //remove file
}
});
$(function() {
$(".dropzone").sortable({
items: '.dz-preview',
cursor: 'move',
opacity: 0.5,
containment: '.dropzone',
distance: 20,
tolerance: 'pointer',
stop: function(event, ui) {
//cloned div
var cloned = $('div#botofform').clone()
$('#botofform').html("") //empty it
//loop through dropzone..
$('.dropzone .dz-complete').each(function() {
var data_id = $(this).data('id') //get data-id
$(cloned).find("input[data-id=" + data_id + "]").clone().appendTo($('#botofform')) //find input which has that data-id and append same to bottmform
});
}
});
});
<link rel="stylesheet" href=" http://code.jquery.com/ui/1.9.1/themes/base/jquery-ui.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/4.3.0/dropzone.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
//Drop zone box
<div class="dropzone" id="my-awesome-dropzone" action="#">
<div class="fallback">
<input name="cimages[]" type="file" multiple />
</div>
<div class="clearfix"></div>
</div>
<div id="botofform"></div>

How to load data from csv file into form field?

I am trying to link my HTML form with my csv file to populate form field automatically. Based on what user selects in first field, second field should be automatically filled with the appropriate value. when the user starts typing in the first field, the input field automatically pulls data from csv file to show available options. Options appear after user completes writing 3 words in the field.
Further, to avoid any CORS issue in code, I have added additional URL in my CSV file URL which makes it accessible by any web application.
I was able to prepare this code with the help of examples available on web. However, my code is not working properly. I tried to solve this problem on my own. But I don't know about coding enough.
Can anyone please help me to solve this problem.
<script>
$(function() { function processData(allText) { var record_num = 2;
// or however many elements there are in each row
var allTextLines = allText.split(/\r\n|\n/); var lines = []; var headings = allTextLines.shift().split(','); while (allTextLines.length > 0) { var tobj = {}, entry; entry = allTextLines.shift().split(','); tobj['label'] = entry[0]; tobj['value'] = entry[1]; lines.push(tobj); } return lines; }
// Storage for lists of CSV Data
var lists = [];
// Get the CSV Content
$.get("https://cors-anywhere.herokuapp.com/www.coasilat.com/wp-content/uploads/2019/06/file.txt ", function(data) { lists = processData(data); }); $("#species").autocomplete({ minLength: 3, source: lists, select: function(event, ui) { $("#species").val(ui.item.label); $("#identifiant").val(ui.item.value); return false; } }); });)
</script>
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<form>
<div class="ui-widget"> <label for="species">Species: </label> <input id="species"> <label for="identifiant">Identifiant: </label> <input id="identifiant" style="width: 6em;"> </div></form>
Here's the modified answer, working with jquery-ui autocomplete.
The solution: the $.get() is an asynchronous function (the data is not readily available on page load), so jquery-ui autocomplete didn't work with the updated lists[] array, because it (seems so that it) doesn't work with dynamically generated data. So the source of autocomplete had to be refreshed with the newly arrived data in the $.get()'s callback function.
$("#species").autocomplete('option', 'source', lists) - this is the key line, as it updates autocomplete's source with the new data.
// Only needed for working example
var myCSV = "Species,Identifiant\r\n";
myCSV += "Species A,320439\r\n";
myCSV += "Species B,349450\r\n";
myCSV += "Species C,43435904\r\n";
myCSV += "Species D,320440\r\n";
myCSV += "Species E,349451\r\n";
myCSV += "Species F,43435905\r\n";
console.log(myCSV);
// Begin jQuery Code
$(function() {
function processData(allText) {
// var record_num = 2; // or however many elements there are in each row
var allTextLines = allText.split(/\r\n|\n/);
var lines = [];
var headings = allTextLines.shift().split(',');
while (allTextLines.length > 0) {
var tobj = {},
entry;
entry = allTextLines.shift().split(',');
/*
Normally we'd read the headers into the object.
Since we will be using Autocomplete, it's looking for an array of objects with 'label' and 'value' properties.
tobj[headings[0]] = entry[0];
tobj[headings[1]] = entry[1];
*/
if (typeof entry[1] !== 'undefined') {
let prefix = !entry[0].includes('Species') ? 'Species ' : ''
tobj['label'] = prefix + entry[0];
tobj['value'] = entry[1].trim();
lines.push(tobj);
}
}
return lines;
}
let lists = [];
// For working example
// lists = processData(myCSV);
// console.log('lists1', lists)
// In your script you will get this content from the CSV File
// Get the CSV Content
$.get("https://cors-anywhere.herokuapp.com/www.coasilat.com/wp-content/uploads/2019/06/file.txt", function(data) {
lists = processData(data);
$("#species").autocomplete('option', 'source', lists)
console.log('lists2', lists)
});
$("#species").autocomplete({
minLength: 3,
source: lists,
focus: function(event, ui) {
console.log(ui)
$("#species").val(ui.item.label);
return false;
},
select: function(event, ui) {
$("#species").val(ui.item.label);
$("#identifiant").val(ui.item.value);
return false;
}
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<link href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" rel="stylesheet" />
<div class="ui-widget">
<label for="species">Species: </label>
<input id="species">
<label for="identifiant">Identifiant: </label>
<input id="identifiant" style="width: 6em;">
</div>
The processData() function didn't work as expected with the source you provided, so that had to be modified too.
My solution is a kinda' autocomplete - it's called typeahead.
I displayed the filtered list, so you see what's happening, but you can place that anywhere - in a dropdown below the input field, for example.
$(function() {
// processing CSV data
function processData(allText) {
// splitting lines
var allTextLines = allText.split(/\r\n|\n/);
const speciesData = []
// reading data into array, if it's not the first row (CSV header) AND
// if it's not 'Species'
let j = 0; // this will be the item's index
for (let i = 0; i < allTextLines.length - 1; i++) {
if (i !== 0 && allTextLines[i] !== 'Species') {
const record = allTextLines[i].split(',')
speciesData.push({
label: record[0],
value: record[1].trim(), // it has a lot of whitespace
index: j // adding this, so we can keep track of items
})
j++; // incrementing index
}
}
// returning processed data
return speciesData;
}
// Storage for lists of processed CSV Data
let lists = [];
// Get the CSV Content
$.get("https://cors-anywhere.herokuapp.com/www.coasilat.com/wp-content/uploads/2019/06/file.txt ", function(data) {
// making processed data availabel app-wide
lists = processData(data);
// filling the 'suggestions list' the first time
suggestionListHtml(lists, $('.suggestions-container'))
});
// actions on input field input event
// only the third param differs in filterSpecies()
$('#species').on('input', function(e) {
const filteredList = filterSpecies($(this).val(), lists, 'label')
suggestionListHtml(filteredList, $('.suggestions-container'))
})
$('#identifiant').on('input', function(e) {
const filteredList = filterSpecies($(this).val(), lists, 'value')
suggestionListHtml(filteredList, $('.suggestions-container'))
})
// clicking on an item in the 'suggestions list' fills out the input fields
$('.suggestions-container').on('click', '.suggestion', function(e) {
const item = lists[$(this).attr('data-listindex')]
$('#species').val(item.label)
$('#identifiant').val(item.value)
})
});
function suggestionListHtml(filteredList, container) {
// creating HTML template for the 'suggestions list'
let html = ''
filteredList.forEach(item => {
html += `<span class="suggestion" data-listindex="${item.index}">label: ${item.label} - value: ${item.value}</span>`
})
// modifying the displayed 'suggestions list'
container
.empty()
.append(html)
}
// filtering the processed list
// #param substr - the text from the input field
// #param list - the list to be filtered
// #param attr - one of the keys in the processed list (label or value)
function filterSpecies(substr, list, attr) {
// doing the actual filtering
const filteredList = list.filter(item => {
return item[attr].toLowerCase().includes(substr.toLowerCase())
})
return filteredList
}
.suggestions-container span {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form>
<div class="ui-widget">
<label for="species">Species: </label>
<input id="species">
<label for="identifiant">Identifiant: </label>
<input id="identifiant" style="width: 6em;">
</div>
<div class="suggestions-container">
</div>
</form>

Custom upload for a generated form

I have a generated form which I can't add HTML directly to it.
This form has 2 upload boxes which needs to have a custom styling. I have managed to style the upload box, but upon testing the function which I need to be triggerd, doesn't trigger.
To clarify, here is my code and what I tried:
The html (that is autom being generated):
<div class="controls">
<input class="span3" type="file" name="img1" id="img1" />
</div>
This is the jQuery
$(document).on('change', 'input[type=file]', function() {
var input = $(this),
numFiles = input.get(0).files ? input.get(0).files.length : 1,
label = input.val().replace(/\\/g, '/').replace(/.*\//, '');
input.trigger('fileselect', [numFiles, label]);
});
$(document).ready(function() {
var fileinput = $('input[type=file]', this);
var file_parent = fileinput.parent(); // get the parent
var fileinputhtml = file_parent.html(); // the HTML of the parent (which is input)
file_parent.replaceWith('<span class="btn btn-file"><span class="btn-class btn-primary">Browse</span>'+fileinputhtml+'<span class="form-control"></span></span>');
// The upload function has been triggerd
fileinput.on('fileselect', function(event, numFiles, label) {
console.log(label); // When the .replaceWith() is active, it doesn't show this log.
var input = $(this).parent().find('.fileupload-filename');
console.log(input.val(label));
if( input.length ) {
input.val( label );
}
});
});
Maybe it has an easy solution but I really can't seem to get this to work.
You help would be highly appericiated

How to bind files to input[type="file"] in javascript

I have div which was dropzone to drop file(s) in the div,
<div id="dropZone"></div>
<input type="file" id=fileUpload"/>
I want to bind drop files to the html file type "fileUpload" control. I have tried like
but not getting. is it possible?
I have written the script for the div
$('#dropZone').bind('dragenter', ignoreDrag);
$('#dropZone').bind('dragover', ignoreDrag);
$('#dropZone').bind('drop', function (e) {
drop(e);
});
Edited and Added script
//Updated
function ignoreDrag(e) {
e.originalEvent.stopPropagation();
e.originalEvent.preventDefault();
}
//Updated End
and in the drop function
function drop(e) {
ignoreDrag(e);
var dt = e.originalEvent.dataTransfer;
var droppedFiles = dt.files;
if (dt.files.length > 0) {
for (var i = 0; i < dt.files.length; i++) {
var fileName = droppedFiles[0].name;
$("#lblMsg").show();
$("#spnFile").text(fileName);
var formData = new FormData();
formData.append("fileUploaded", droppedFiles);
alert(dt.files[0]);
$("#fileUploaded").bind("val", dt.files[0]);
// Binding to file(s) to the <input type="file"/> but not binding.
}
}
}
Is this possible to bind like above??

Clone form input and clear value

I'm trying to create an upload form. It's working well so far and i'm trying to sort out a couple of bugs that I dislike.
The line that I seem to be having trouble with is
$(element).find(">:first-child").attr("value", "");
When cloning the form, it clones the div and replaces the value with nothing leaving a blank form, this works well, if I were to delete that line I would get the previous form's value, so it would be nice for a blank form to show.
The issue i'm having is when you delete a form all the forms values delete, What I want is when you delete a form, leave the value alone for the other forms.
Here's a fiddle http://jsfiddle.net/d77pd/1/ or see code below
HTML
<button class="clone">Add an Image</button>
<div id="upload_image_sets">
<div id="clonedInput1" class="clonedInput">
<input type="text" id="upload_image_link_1" class="image" size="36" name="hero_options[upload_image_link_1]" value="' . $hero_options['upload_image_link_1'] . '" />
<input id="show_upload_image_link_button_1" class="button upload_images" type="button" value="Upload Image" />
<button class="remove">Remove</button>
</div>
</div>
JavaScript
function updateClonedInput(index, element) {
$(element).appendTo("#upload_image_sets").attr("id", "clonedInput" + index);
$(element).find(">:first-child").attr("id", "cs_product_menu_img_src_" + index);
$(element).find(">:first-child").attr("name", "hero_options[upload_image_link_" + index + "]");
$(element).find(">:first-child").attr("value", "");
$(element).find(">:first-child").next().attr("id", "cs_product_menu_img_src_" + index + "_button");
displayRemove();
}
function displayRemove() {
if ($('.clonedInput').length === 1) {
$('.remove').hide();
} else {
$('.remove').show();
}
}
displayRemove();
$(document).on("click", ".clone", function (e) {
e.preventDefault();
var cloneIndex = $(".clonedInput").length + 1;
var new_Input = $(this).closest('.clonedInput').length ? $(this).closest('.clonedInput').clone() : $(".clonedInput:last").clone();
updateClonedInput(cloneIndex, new_Input);
});
$(document).on("click", ".remove", function (e) {
e.preventDefault();
$(this).parents(".clonedInput").remove();
$(".clonedInput").each(function (cloneIndex, clonedElement) {
updateClonedInput(cloneIndex + 1, clonedElement);
})
});
Clone the form a few times, if you delete any form apart form the 1st one with the content, you'll notice the first form's content deletes, I want this left alone.
First approach:
call $(element).find(">:first-child").attr("value", ""); after calling updateClonedInput(cloneIndex, new_Input); from add function.
Working Demo First approach:
Second Approach:
I have modified some code. pass one more bool argument in function updateClonedInput.which will be set true when added and set false when dom is removed.This will prevent the value getting replaced on remove function:
function updateClonedInput(index, element,param) {
$(element).appendTo("#upload_image_sets").attr("id", "clonedInput" + index);
$(element).find(">:first-child").attr("id", "cs_product_menu_img_src_" + index);
$(element).find(">:first-child").attr("name", "hero_options[upload_image_link_" + index + "]");
if(param)
$(element).find(">:first-child").attr("value", "");
$(element).find(">:first-child").next().attr("id", "cs_product_menu_img_src_" + index + "_button");
displayRemove();
}
function displayRemove() {
if($('.clonedInput').length === 1) {
$('.remove').hide();
} else {
$('.remove').show();
}
}
displayRemove();
$(document).on("click", ".clone", function(e){
e.preventDefault();
var cloneIndex = $(".clonedInput").length + 1;
var new_Input = $(this).closest('.clonedInput').length ? $(this).closest('.clonedInput').clone() : $(".clonedInput:last").clone();
updateClonedInput(cloneIndex, new_Input,true);
});
$(document).on("click", ".remove", function(e){
e.preventDefault();
$(this).parents(".clonedInput").remove();
$(".clonedInput").each( function (cloneIndex, clonedElement) {
updateClonedInput(cloneIndex + 1, clonedElement,false);
})
});
Working Demo Second Approach
An alternate solution that creates a blank clone from the first element once, then uses this every time a new row is required. It also uses CSS to hide/show the Remove button based on the fact that you only need Remove buttons on all rows unless it's the only child.
Disclaimer: I have removed the id manipulation as I am unsure if you really need it. I can update if necessary.
Demo
HTML
<button class="clone">Add an Image</button>
<div id="upload_image_sets">
<div class="clonedInput">
<input type="text" class="image" size="36" name="hero_options[upload_image_link_1]" value="an initial value" />
<input class="button upload_images" type="button" value="Upload Image" />
<button class="remove">Remove</button>
</div>
</div>
CSS
.clonedInput .remove {
display:inline-block;
}
.clonedInput:only-child .remove {
display:none;
}
JavaScript
function resetForm($form) {
$form.find('input:text, input:password, input:file, select, textarea').val('');
$form.find('input:radio, input:checkbox').removeAttr('checked').removeAttr('selected');
}
var $blankClone = $('.clonedInput').clone();
resetForm($blankClone);
$(document).on('click', '.clone', function(e) {
e.preventDefault();
$blankClone.clone().appendTo('#upload_image_sets');
});
$('#upload_image_sets').on('click', '.remove', function(e) {
e.preventDefault();
$(this).closest('.clonedInput').remove();
});
resetForm() borrowed from Resetting a multi-stage form with jQuery

Categories

Resources