I’ve been working on an angular project that has a requirement to allow users to upload documents, pictures and videos to the site. As a part of the upload process, after the user has selected their file, but before it’s actually uploaded to the server, the user needs to supply some metadata about the file. Being a responsive, highly stylized site, the client didn’t want the standard input control with type=”file”. Instead they wanted the user to click an “Add file”-type button that would pop up the explorer window for the user to pick a file, and then once the file is picked, prompt the user for the required metadata before performing the actual upload.
I ended up including flowjs (https://github.com/flowjs
) in the project, which is quite a nice file upload library, and is usable as an angular directive! Flowjs has a lot of nice uploading features, but this client had its own custom upload process, so I couldn’t use flowjs for the uploading piece. This meant all I ended up really using it for was to handle prompting the user to select the file, and limiting the file selection options (just images, just videos, all, etc). It worked great – or so we thought.
Flowjs accomplishes prompting the user to pick a file to upload by dynamically inserting an input field with type=”file” into the page and hiding it. Then, when the user performs whatever action should trigger the file selection (usually a click event) flowjs would programmatically click the file input field. This was working great, until I found a bug with the file input field.
Like most field controls in HTML, the way a developer determines if something has changed in the field is listening for the “change” event of the field. The same is true for the file input field. Flowjs would trigger an event when the user picks a file by listening for “change” on the file input. I was listening for this event from flowjs in order to pop up a dialog window prompting the user for metadata about the file. What was discovered, however, was if the user accidentally closed the dialog window then tried to pick the SAME file they had just picked, the metadata prompt wouldn’t open again! If the user selected a different file, it would work just fine – it was only if the same file was selected twice in a row that this issue would occur.
The first thing I investigated was flowjs. Very quickly I realized it wasn’t flowjs that was the issue, it was the file input control itself. Thinking about it, it makes complete sense – we only know the user selected a file if the “change” event on the file input control fires. If the user selects the same file that is already selected in the control, obvious nothing changes, right? So the “change” event wouldn’t occur. The answer seemed obvious at that point – just clear out the field after the user is done with it.
It ended up not being that simple. For whatever reason, even if you clear the input file’s value (thus making it LOOK like it’s been reset), the “change” event STILL wouldn’t fire if the user selected the same file again. A little research quickly revealed that we were far from the first to notice this counter-intuitive behavior. One of the workarounds I found was to actively remove the file input from the DOM, then dynamically create a new one and insert it, effectively replacing the file input with a “fresh” version. This of course meant I was now looking to do things outside of what flowjs provides, so it was time to write my own directive.
This worked beautifully. Now the user could select the same file as many times as they wanted in a row and the “change” event would fire correctly every time.