Se você quiser algo um pouco mais elegante / integrado, use um decorador para estender a input
diretiva com suporte type=file
. A principal ressalva a ser lembrada é que esse método não funcionará no IE9, pois o IE9 não implementou a API de arquivos . O uso de JavaScript para fazer upload de dados binários, independentemente do tipo via XHR, simplesmente não é possível nativamente no IE9 ou anterior (uso deActiveXObject
para acessar o sistema de arquivos local não conta, pois o uso do ActiveX está apenas solicitando problemas de segurança).
Esse método exato também requer o AngularJS 1.4.x ou posterior, mas você pode adaptá-lo para usá-lo em $provide.decorator
vez de angular.Module.decorator
- escrevi essa essência para demonstrar como fazê-lo, em conformidade com o guia de estilo do John Papa no AngularJS :
(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;
};
}
})();
Por que isso não foi feito em primeiro lugar? O suporte ao AngularJS deve atingir apenas o IE9. Se você não concorda com essa decisão e acha que eles deveriam simplesmente colocar isso de qualquer maneira, vá para o Angular 2+, pois melhor suporte moderno é literalmente o motivo pelo qual o Angular 2 existe.
O problema é (como mencionado anteriormente) que, sem o suporte da API do arquivo, fazer isso corretamente é inviável para o núcleo, dado que nossa linha de base é o IE9 e o preenchimento de polifill está fora de questão.
Além disso, tentar lidar com essa entrada de uma maneira que não seja compatível com vários navegadores apenas dificulta as soluções de terceiros, que agora precisam combater / desativar / solucionar a solução principal.
...
Eu vou fechar isso assim que fechamos o # 1236. O Angular 2 está sendo construído para oferecer suporte a navegadores modernos e com esse suporte a arquivos estará facilmente disponível.