What we want to achieve:
- styling file input
- get the name(s) of files, since we aren’t depending on the original input to tell us what our user has uploaded
- bonus: read and display the text content of file
Styling Input File
For styling, simple solution is to use label.
<label class="import-file"> <input type="file" multiple onchange="angular.element(this).scope().fileNameChanged(this)"> <div>Import file...</div> </label>
Use display: none;
on the input, and have your wicked way with the div.
Get the List of File Names
Since Angular doesn’t naturally bind this input, use onchange on the input, like above. Then in controller:
$scope.fileNameChanged = function (ele) { var files = ele.files; var l = files.length; var namesArr = []; for (var i = 0; i < l; i++) { namesArr.push(files[i].name); } }
Basically the element.files is an object like this:
I’d like the string of file names displayed, so this it is:
$scope.namesString = namesArr.join(' ,'); $scope.$apply();
The only thing to pay attention to is the $apply(). Since this wasn’t part of Angular’s watch, we need to do this manually.
Bonus: Read and Display Text Content of File
To accomplish this, we’ll need HTML5’s feature: the FileRead object. Which means IE probably messed it up somewhere (no IE9 support alert).
First, we need to create an instance of FileRead, and set onload.
var reader = new FileReader(); reader.onload = function () { $scope.fileContent = reader.result; $scope.$apply(); }
And, since this is a callback, we need to call $apply at the bottom here instead of at the end of fileNameChanged().
Then, we trigger the read/load. Note that this works with only one file at a time.
reader.readAsText(element.files[0]);
There’re multiple ways to trigger a file read. You can read the content as ArrayBuffer, BinaryString, DataURL and Text string, depending on your need. See the full method list here, and see all the demos here.