I've taken Cherniv's code and folded all of it into a directive:
.directive('fileSelect', ['$window', function ($window) { return { restrict: 'A', require: 'ngModel', link: function (scope, el, attr, ctrl) { var fileReader = new $window.FileReader(); fileReader.onload = function () { ctrl.$setViewValue(fileReader.result); if ('fileLoaded' in attr) { scope.$eval(attr['fileLoaded']); } }; fileReader.onprogress = function (event) { if ('fileProgress' in attr) { scope.$eval(attr['fileProgress'], {'$total': event.total, '$loaded': event.loaded}); } }; fileReader.onerror = function () { if ('fileError' in attr) { scope.$eval(attr['fileError'], {'$error': fileReader.error}); } }; var fileType = attr['fileSelect']; el.bind('change', function (e) { var fileName = e.target.files[0]; if (fileType === '' || fileType === 'text') { fileReader.readAsText(fileName); } else if (fileType === 'data') { fileReader.readAsDataURL(fileName); } }); } }; }]);
You can then use the directive as follows:
<input type="file" ng-model="file.data" file-select="data" file-loaded="myLoaded()" file-error="myError($error)" file-progress="myProgress($total,$loaded)">
Where "file.data", "myLoaded", "myError", and "myProgress" are in the enclosing scope.