Angular doesn't support binding to file-type inputs, but I cobbled together a solution using a number of other answers.
app.directive('filePicker', filePicker);
filePicker.$inject = ['$log', '$document'];
function filePicker($log,$document) {
var directive = {
restrict: 'A',
require: 'ngModel',
scope: {
ngModel: '='
},
link: _link
};
return directive;
function _link(scope, elem, attrs, ngModel) {
// check if valid input element
if( elem[0].nodeName.toLowerCase() !== 'input' ) {
$log.warn('filePicker:', 'The directive will work only for input element, actual element is a', elem[0].nodeName.toLowerCase());
return;
}
// check if valid input type file
if( attrs.type != 'file' ) {
$log.warn('filePicker:', 'Expected input type file, received instead:', attrs.type, 'on element:', elem);
return;
}
// listen for input change
elem.on('change', function(e) {
// get files
var files = elem[0].files;
// update model value
scope.$apply(function() {
attrs.multiple ? scope.ngModel = files : scope.ngModel = files[0];
});
});
scope.$watch('ngModel', function() {
if (!scope.ngModel)
elem[0].value = ""; // clears all files; there's no way to remove only some
});
}
}
This solution showed me how to use a directive to implement a custom binding to ng-model. It enables accessing the contents of the file, so if you need that functionality you can add it back to my solution.
However, it had some problems with its binding. It would correctly set the value of my variable_in_scope
, but if there were other things bound to the value of variable_in_scope
, they wouldn't update. The trick was to use isolate scope and $apply
. Then you don't need to mess with this $setViewValue
business. Just set it and forget it.
That got me as far as one-way-binding. If I set a value to variable_in_scope
, however, the file picker still showed that I had the original file selected. In my case all I really want to do is clear the selected file. I found out the Javascript magic to do this and set up a $watch
on the ngModel
to trigger it.
If you want to set the file to a different value programmatically, good luck to you, because FileList
is read-only. The magic trick lets you clear the FileList
, but you can't add anything back. Maybe you can create a new FileList
and assign it to .files
, but at a cursory glance I didn't see a way to do that.