Ticket #2531877 (closed enhancement)

Reporter


Brad Kusnir
Opened: 03/9/12
Last modified: 08/1/12
Status: closed
Type: enhancement
Resolution: fixed

Owner


Allen Rabinovich
Target Release: 3.6.0
Priority: P3 (normal)
Summary: File List Manipulation
Description:

There is no mechanism to control and manipulate the built in (multiple) file list that the uploader control uses. You can add files, but you cannot remove them. It is pretty easy to add an event to
clear the list entirely, but I am finding it much more challenging to remove individual files from the list using the out of the box solution. This also makes it difficult to perform validations
(i.e. file size, file extension, max # of files) before adding a file to the list. I am going to build a solution for my implementation to manipulate an array of Y.File objects to allow for these
adjustments and use UploadThese to send them to our backend.

Type: enhancement Observed in Version: 3.5.0pr2
Component: Uploader Severity: S3 (normal)
Assigned To: Allen Rabinovich Target Release: 3.6.0
Location: Priority: P3 (normal)
Tags: Relates To:
Browsers: N/A
URL:
Test Information:

/*
modified stock manual test examples - part of the problem is that the list only has a listener that fires after the file selection dialog has passed.

  • /

// event listener to watch if file list changes
myuploader.after("fileListChange", function (ev) {
// clear file list output
out.setContent("");
// clear POST variables array
postVars = [];
Y.each(myuploader.get("fileList"), function (value) {
out.append("<div id='" + value.get("id") + "'>" + value.get("name") + " | " + 0 + "% <button type=button id=clearButton>Clear</button></div>");
postVars[value.get("id")] = {filename: value.get("name"), filesize: value.get("size")};
});
myuploader.set("postVarsPerFile", postVars);
//validateFiles();
// show clear all button
document.getElementById("clearAllButton").style.display = "block";
});

/*
I have tried to institute a".before" listener but doesn't seem to have the desired effect. Notice the bahavior after selecting the first file - no information is output because the Y.File object is
not yet in the array. Select the second file - you will see the first Y.File object has been output now. This presents challenges for trying to perform validations BEFORE the file has been added to
the array.

  • /

myuploader.before("fileListChange", function (ev) {
console.log("before we let the file list change do this");
console.log(myuploader.get("fileList"));
Y.each(myuploader.get("fileList"), function (value)
{
// check for valid file extension
console.log(value.get("name"));
// check for valid file size
console.log(value.get("size"));
});
});

/*
event listener for "Clear All" button - I need a solution to be able to remove individual files from the list as well. The only way I see doing that right now is to do my own array management and
pass it to uploadThese() for uploading.

  • /

Y.one("#clearAllButton").on("click", function () {
// clear file list output
out.setContent("");
// clear POST variables array
postVars = [];
// clear file list array
fileList = [];
// clear file list
myuploader.set("fileList", fileList);
});

/*
for limiting the number of files i could wire an event listener to the "Select Files" button and call this function and try to break out of the file dialogue somehow - but this does not solve the file
extension or file size validation issue.

  • /

// returns number of files being uploaded so backend aggregator knows when all files have been received via async XHR xfer
function numFiles() {
// console.log("numFiles() start");
var nf = 0;
Y.each(myuploader.get("fileList"), function (value)
{
nf++;
// console.log("number of files:" + nf);
});
// console.log("numFiles() stop");
return nf;
}

Change History

Allen Rabinovich

YUI Developer

Posted: 03/27/12
  • priority changed to P3 (normal)
  • status changed from new to accepted

Brad, it sounds like the best and easiest way to go about this is to make fileList a property, rather than an attribute? What do you think? I can have the attribute shadow the property, but otherwise you'd be able to directly manipulate the property like a regular array.

Allen Rabinovich

YUI Developer

Posted: 03/30/12
  • milestone changed to 3.6.0

Brad Kusnir

Posted: 04/7/12

Hey Allen sorry for the delayed response - I am just getting to this now. I am very close to replacing my previous HTML4/CSS2/POJO solution with the YUI 3.5pr2 HTML5 uploader control (with Flash fallback).

As for your suggestion I think that could work. Would be nice to have a method you can call to basically just say "remove element with fileID X" or maybe include a simple file number attribute that could be referenced similarly?

The requirement I am struggling with is that if before a user submits a batch for upload, if they decide they want to remove one of the files in the list this is not easily accomplished. It would be very nice if there was a built in facility to accomplish this - right now the only thing that's easy to do is clear the array entirely:

// clear file list array
fileList = [];
// clear file list
myuploader.set("fileList", fileList);

But perhaps I just need to look into handling this within my own code but not sure how to reference the file list object directly... I assume there is a corresponding get method that I can call, copy to an array var, work with it, and then use the set method again.

I also find myself using the before/after list change function to perform validations (i.e. iterating through to count number of total files, parse extensions, file sizes). I don't think this is the proper / most efficient way to go about this...

Allen Rabinovich

YUI Developer

Posted: 05/8/12

This will become either a property instead of an attribute, or we'll add convenience methods for managing the attribute (removeFiles, get items at index, etc.).

Allen Rabinovich

YUI Developer

Posted: 05/8/12
  • estimated changed from 0 to 2
  • remaining changed from 0 to 2
  • sprint changed to sprint 2

Brad Kusnir

Posted: 05/10/12

Awesome! Here is what I ended up doing to implement this:

// clear a single file
function clearFile(position) {
// clear POST variable array element
postVars = [];
// declare an array for the temporary (working) file list
fileList = [];
// get the file list from the uploader
fileList = myuploader.get("fileList", fileList);
// delete the selected file from the array
fileList.splice(position, 1);
// set file list back
myuploader.set("fileList", fileList);
// redraw new file list
drawFileList();
// remove "Clear All" button
if (fileList.length < 1) {
Y.one("#clearAllButton").hide();
Y.one("#clearAllBtn").hide();
}
}

// draw file list
function drawFileList() {
out.setContent("");
// clear POST variables array
postVars = [];
var showClearAll = false;

// clear file list output
out.setContent([
'<table ID=UploadPageSelf width=100% border=0 cellspacing=0 cellpadding=0>',
' <tr>',
' <td>',
' <br />',
' </td>',
' </tr>',
' <tr>',
' <td>'
].join());

Y.each(myuploader.get("fileList"), function (value, fileNum) {
fileId = value.get("id");
fileName = value.get("name");
fileSize = Math.floor(value.get("size") / 1024);
out.append([
' <div class=fileNum id=', fileId, '>',
' <label for=file>File', (fileNum + 1), ':</label></div>',
' <div class=fileName>', fileName, '</div> ',
' </td>',
' <td width=50%>',
' <a href=# class=clearLink id=', fileNum, '>Clear</a>',
' <div class=fillerFive> </div>',
' <div class=fillerTen> </div>',
' </td>',
' </tr>',
'</table>'
].join(
));
// set showClearAll button flag
showClearAll = true;

// TO DO: consider adding TOTAL filesize
});
myuploader.set("postVarsPerFile", postVars);
// run validate files function to prevent illegal files from being added and provided instant user feedback
validateFiles();
}

//// event listeners

// event delegate for "Clear" link
out.delegate('click', function (ev) {
var position = ev.target.get('id').replace('Clear', '');
clearFile(position);
}, 'a.clearLink');

Allen Rabinovich

YUI Developer

Posted: 06/9/12
  • status changed from accepted to checkedin

There's now a private property _fileList which, if strictly necessary, can be manipulated directly. It's still recommended to retrieve the fileList by calling get("fileList"), manage it directly as an array, and then set("fileList") back, but a direct manipulation option is also available. Additional array manipulation helper methods will be added in future releases.

Jenny Donnelly

YUI Developer

Posted: 08/1/12
  • resolution changed to fixed
  • status changed from checkedin to closed

Shipped in 3.6.0. Marking closed/fixed.