I have a form that takes file uploads and it currently has a limit of 10 files per upload. There are PHP validations in the backend for this too.
When more than 10 files are attached, I currently have a JavaScript slice(0, 10) method inside a change event for the file input element, which removes any files (and their preview image thumbnails) when the number attached is more than 10 files.
// For each added file, add it to submitData (the DataTransfer Object), if not already present
[...e.target.files].slice(0,10).forEach((file) => {
if (currentSubmitData.every((currFile) => currFile.name !== file.name)) {
submitData.items.add(file);
}
});
The Issue
What I can’t seem to do though is work out a way to slice() the files array in a compound attachment situation, i.e. if 8 files are attached initially, and then the user decides to add another 4 prior to submitting the form, taking the total to 12. The current slice only happens when more than 10 are added in one go.
I have a decode() method that runs inside a loop (for every image attached) that carries out frontend validations, and a promiseAllSettled() method that waits for the last image to be attached prior to outputting a main error message telling the user to check the specific errors on the page.
Question
How do I slice the array on the total number of files appended, if the user has initially attached a file count less than 10, then attaches further files taking it more than 10 prior to form submission?
const attachFiles = document.getElementById('attach-files'), // file input element
dropZone = document.getElementById('dropzone'),
submitData = new DataTransfer();
dropZone.addEventListener('click', () => {
// assigns the dropzone to the hidden 'files' input element/file picker
attachFiles.click();
});
attachFiles.addEventListener('change', (e) => {
const currentSubmitData = Array.from(submitData.files);
console.log(e.target.files.length);
// For each added file, add it to 'submitData' if not already present (maximum of 10 files with slice(0, 10)
[...e.target.files].slice(0,10).forEach((file) => {
if (currentSubmitData.every((currFile) => currFile.name !== file.name)) {
submitData.items.add(file);
}
});
// Sync attachFiles FileList with submitData FileList
attachFiles.files = submitData.files;
// Clear the previewWrapper before generating new previews
previewWrapper.replaceChildren();
// the 'decode()' function inside the 'showFiles()' function is returned
// we wait for all of the promises for each image to settle
Promise.allSettled([...submitData.files].map(showFiles)).then((results) => {
// output main error message at top of page alerting user to error messages attached to images
});
}); // end of 'change' event listener
function showFiles(file) {
// code to generate image previews and append them to the 'previewWrapper'
// then use the decode() method that returns a promise and do JS validations on the preview images
return previewImage.decode().then(() => {
// preform JS validations and append
}).catch((error) => {
console.log(error)
});
} // end of showfiles(file)
Instead of looking into your whole code, Here I came up with the solution/Suggestion as per looking into a specific piece of code.
Once you spliced the initial set of files, After that you can check how many files are remaining and then you can pass the second parameter in the splice dynamically based on the required number of files.
Steps :
Create a separate empty array and insert first selection of files into that array on change event.
Now check for this newly created array length, if there is any number of elements then you can update the 2nd splice method parameter like this :
secondParameter = secondParameter - newArr.length
Live Demo :
let splicedArrCount = 10;
let resArr = [];
function spliceArr() {
const inputVal = Number(document.getElementById('number').value);
if (inputVal) {
const arr = Array(inputVal).fill(inputVal);
if (!resArr.length) {
resArr = [...arr];
} else if (resArr.length) {
splicedArrCount = splicedArrCount - resArr.length
resArr.push(...arr.splice(0, splicedArrCount));
}
}
console.log(resArr);
}
<input type="number" id="number" onChange="spliceArr()"/>
In the above demo, You can do a testing by inserting a number into a input box, which will convert the number into an array elements. For ex. If you will enter 8, it will create an array with 8 elements and then if you pass 4, it will update an array. i.e. [8, 8, 8, 8, 8, 8, 8, 8, 4, 4]
We can keep the count of the number of files already added.
Instead of [...e.target.files].slice(0,10).forEach((file) => {}, we can do something like:
var numberOfFilesToAdd = 10 - currentSubmitData.length;
[...e.target.files].slice(0,numberOfFilesToAdd).forEach((file) => {}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With