If you want something a little more elegant/integrated, you can use a decorator to extend the input directive with support for type=file. The main caveat to keep in mind is that this method will not work in IE9 since IE9 didn't implement the File API. Using JavaScript to upload binary data regardless of type via XHR is simply not possible natively in IE9 or earlier (use of ActiveXObject to access the local filesystem doesn't count as using ActiveX is just asking for security troubles).
This exact method also requires AngularJS 1.4.x or later, but you may be able to adapt this to use $provide.decorator rather than angular.Module.decorator - I wrote this gist to demonstrate how to do it while conforming to John Papa's AngularJS style guide:
(function() {
'use strict';
/**
* @ngdoc input
* @name input[file]
*
* @description
* Adds very basic support for ngModel to `input[type=file]` fields.
*
* Requires AngularJS 1.4.x or later. Does not support Internet Explorer 9 - the browser's
* implementation of `HTMLInputElement` must have a `files` property for file inputs.
*
* @param {string} ngModel
* Assignable AngularJS expression to data-bind to. The data-bound object will be an instance
* of {@link https://developer.mozilla.org/en-US/docs/Web/API/FileList `FileList`}.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} ngChange
* AngularJS expression to be executed when input changes due to user interaction with the
* input element.
*/
angular
.module('yourModuleNameHere')
.decorator('inputDirective', myInputFileDecorator);
myInputFileDecorator.$inject = ['$delegate', '$browser', '$sniffer', '$filter', '$parse'];
function myInputFileDecorator($delegate, $browser, $sniffer, $filter, $parse) {
var inputDirective = $delegate[0],
preLink = inputDirective.link.pre;
inputDirective.link.pre = function (scope, element, attr, ctrl) {
if (ctrl[0]) {
if (angular.lowercase(attr.type) === 'file') {
fileInputType(
scope, element, attr, ctrl[0], $sniffer, $browser, $filter, $parse);
} else {
preLink.apply(this, arguments);
}
}
};
return $delegate;
}
function fileInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
element.on('change', function (ev) {
if (angular.isDefined(element[0].files)) {
ctrl.$setViewValue(element[0].files, ev && ev.type);
}
})
ctrl.$isEmpty = function (value) {
return !value || value.length === 0;
};
}
})();
The issue is (as was mentioned before) that without the file api
support doing this properly is unfeasible for the core given our
baseline being IE9 and polyfilling this stuff is out of the question
for core.
Additionally trying to handle this input in a way that is not
cross-browser compatible only makes it harder for 3rd party solutions,
which now have to fight/disable/workaround the core solution.
...
I'm going to close this just as we closed #1236. Angular 2 is being
build to support modern browsers and with that file support will
easily available.