ng-model for ' <input type="file"/> '(带有演示指令)

我尝试使用ng-model的输入标签类型文件:

<input type="file" ng-model="vm.uploadme" />

但是在选择了一个文件后,在控制器中,$scope.vm。Uploadme仍然没有定义。

我如何在我的控制器中获得所选文件?

323214 次浏览

我用指令创建了一个变通方法:

.directive("fileread", [function () {
return {
scope: {
fileread: "="
},
link: function (scope, element, attributes) {
element.bind("change", function (changeEvent) {
var reader = new FileReader();
reader.onload = function (loadEvent) {
scope.$apply(function () {
scope.fileread = loadEvent.target.result;
});
}
reader.readAsDataURL(changeEvent.target.files[0]);
});
}
}
}]);

input标签变成:

<input type="file" fileread="vm.uploadme" />

或者如果只需要文件定义:

.directive("fileread", [function () {
return {
scope: {
fileread: "="
},
link: function (scope, element, attributes) {
element.bind("change", function (changeEvent) {
scope.$apply(function () {
scope.fileread = changeEvent.target.files[0];
// or all selected files:
// scope.fileread = changeEvent.target.files;
});
});
}
}
}]);

这是@endy-tjahjono解决方案的补充。

我最终无法从作用域获取uploadme的值。即使HTML中的uploadme被该指令明显地更新了,我仍然无法通过$scope.uploadme访问它的值。不过,我可以从作用域设置它的值。神秘的,对吧?

结果是,该指令创建了子作用域,并且子作用域有自己的uploadme

解决方案是使用对象而不是原语来保存uploadme的值。

在控制器中我有:

$scope.uploadme = {};
$scope.uploadme.src = "";

在HTML中:

 <input type="file" fileread="uploadme.src"/>
<input type="text" ng-model="uploadme.src"/>

指令没有变化。

现在,一切都像预期的那样。我可以使用$scope.uploadme从控制器中获取uploadme.src的值。

我使用这个指令:

angular.module('appFilereader', []).directive('appFilereader', function($q) {
var slice = Array.prototype.slice;


return {
restrict: 'A',
require: '?ngModel',
link: function(scope, element, attrs, ngModel) {
if (!ngModel) return;


ngModel.$render = function() {};


element.bind('change', function(e) {
var element = e.target;


$q.all(slice.call(element.files, 0).map(readFile))
.then(function(values) {
if (element.multiple) ngModel.$setViewValue(values);
else ngModel.$setViewValue(values.length ? values[0] : null);
});


function readFile(file) {
var deferred = $q.defer();


var reader = new FileReader();
reader.onload = function(e) {
deferred.resolve(e.target.result);
};
reader.onerror = function(e) {
deferred.reject(e);
};
reader.readAsDataURL(file);


return deferred.promise;
}


}); //change


} //link
}; //return
});

然后像这样调用它:

<input type="file" ng-model="editItem._attachments_uri.image" accept="image/*" app-filereader />

属性(editItem.editItem._attachments_uri.image)将使用您选择的文件内容填充为data-uri(!)。

请注意,这个脚本不会上传任何东西。它只会用数据uri (base64)编码的文件内容填充模型。

在这里检查一个工作演示: http://plnkr.co/CMiHKv2BEidM9SShm9Vv < / p >

我必须在多个输入上做同样的事情,所以我更新了@Endy Tjahjono方法。 返回一个包含所有已读文件的数组

  .directive("fileread", function () {
return {
scope: {
fileread: "="
},
link: function (scope, element, attributes) {
element.bind("change", function (changeEvent) {
var readers = [] ,
files = changeEvent.target.files ,
datas = [] ;
for ( var i = 0 ; i < files.length ; i++ ) {
readers[ i ] = new FileReader();
readers[ i ].onload = function (loadEvent) {
datas.push( loadEvent.target.result );
if ( datas.length === files.length ){
scope.$apply(function () {
scope.fileread = datas;
});
}
}
readers[ i ].readAsDataURL( files[i] );
}
});


}
}
});

这是一个稍微修改过的版本,允许你在作用域中指定属性的名称,就像你在ng-model, usage中所做的一样:

    <myUpload key="file"></myUpload>

指令:

.directive('myUpload', function() {
return {
link: function postLink(scope, element, attrs) {
element.find("input").bind("change", function(changeEvent) {
var reader = new FileReader();
reader.onload = function(loadEvent) {
scope.$apply(function() {
scope[attrs.key] = loadEvent.target.result;
});
}
if (typeof(changeEvent.target.files[0]) === 'object') {
reader.readAsDataURL(changeEvent.target.files[0]);
};
});


},
controller: 'FileUploadCtrl',
template:
'<span class="btn btn-success fileinput-button">' +
'<i class="glyphicon glyphicon-plus"></i>' +
'<span>Replace Image</span>' +
'<input type="file" accept="image/*" name="files[]" multiple="">' +
'</span>',
restrict: 'E'


};
});

function filesModelDirective(){
return {
controller: function($parse, $element, $attrs, $scope){
var exp = $parse($attrs.filesModel);
$element.on('change', function(){
exp.assign($scope, this.files[0]);
$scope.$apply();
});
}
};
}
app.directive('filesModel', filesModelDirective);

对于使用lodash或下划线输入的多个文件:

.directive("fileread", [function () {
return {
scope: {
fileread: "="
},
link: function (scope, element, attributes) {
element.bind("change", function (changeEvent) {
return _.map(changeEvent.target.files, function(file){
scope.fileread = [];
var reader = new FileReader();
reader.onload = function (loadEvent) {
scope.$apply(function () {
scope.fileread.push(loadEvent.target.result);
});
}
reader.readAsDataURL(file);
});
});
}
}
}]);

我创建了一个指令并在bower上注册。

这个库将帮助您建模输入文件,不仅返回文件数据,而且还返回文件dataurl或base64。

{
"lastModified": 1438583972000,
"lastModifiedDate": "2015-08-03T06:39:32.000Z",
"name": "gitignore_global.txt",
"size": 236,
"type": "text/plain",
"data": "data:text/plain;base64,DQojaWdub3JlIHRodW1ibmFpbHMgY3JlYXRlZCBieSB3aW5kb3dz…xoDQoqLmJhaw0KKi5jYWNoZQ0KKi5pbGsNCioubG9nDQoqLmRsbA0KKi5saWINCiouc2JyDQo="
}

https://github.com/mistralworks/ng-file-model/

如何使<input type="file">ng-model一起工作

使用ng-model的指令的工作演示

核心ng-model指令不能与<input type="file">一起开箱即用。

这个自定义指令启用了ng-model,并具有使ng-changeng-requiredng-form指令与<input type="file">一起工作的额外好处。

angular.module("app",[]);


angular.module("app").directive("selectNgFiles", function() {
return {
require: "ngModel",
link: function postLink(scope,elem,attrs,ngModel) {
elem.on("change", function(e) {
var files = elem[0].files;
ngModel.$setViewValue(files);
})
}
}
});
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app">
<h1>AngularJS Input `type=file` Demo</h1>
    

<input type="file" select-ng-files ng-model="fileArray" multiple>


<code><table ng-show="fileArray.length">
<tr><td>Name</td><td>Date</td><td>Size</td><td>Type</td><tr>
<tr ng-repeat="file in fileArray">
<td>\{\{file.name}}</td>
<td>\{\{file.lastModified | date  : 'MMMdd,yyyy'}}</td>
<td>\{\{file.size}}</td>
<td>\{\{file.type}}</td>
</tr>
</table></code>
    

</body>

我必须修改Endy的指令,这样我就可以得到最后修改,lastModifiedDate,名称,大小,类型和数据,以及能够得到一个文件数组。对于那些需要这些额外功能的人,这里就是。

< p >更新: 我发现了一个错误,如果你选择了文件,然后再次选择但取消,这些文件永远不会像它出现时那样取消选择。所以我更新了我的代码来解决这个问题
 .directive("fileread", function () {
return {
scope: {
fileread: "="
},
link: function (scope, element, attributes) {
element.bind("change", function (changeEvent) {
var readers = [] ,
files = changeEvent.target.files ,
datas = [] ;
if(!files.length){
scope.$apply(function () {
scope.fileread = [];
});
return;
}
for ( var i = 0 ; i < files.length ; i++ ) {
readers[ i ] = new FileReader();
readers[ i ].index = i;
readers[ i ].onload = function (loadEvent) {
var index = loadEvent.target.index;
datas.push({
lastModified: files[index].lastModified,
lastModifiedDate: files[index].lastModifiedDate,
name: files[index].name,
size: files[index].size,
type: files[index].type,
data: loadEvent.target.result
});
if ( datas.length === files.length ){
scope.$apply(function () {
scope.fileread = datas;
});
}
};
readers[ i ].readAsDataURL( files[i] );
}
});


}
}
});

试试这个,这在angular JS中对我有用

    let fileToUpload = `${documentLocation}/${documentType}.pdf`;
let absoluteFilePath = path.resolve(__dirname, fileToUpload);
console.log(`Uploading document ${absoluteFilePath}`);
element.all(by.css("input[type='file']")).sendKeys(absoluteFilePath);

如果你想要一些更优雅/集成的东西,你可以使用装饰来扩展input指令,支持type=file。要记住的主要警告是这个方法在IE9中不起作用,因为IE9没有实现File API. c。在IE9或更早的版本中,使用JavaScript通过XHR上传二进制数据(无论哪种类型)是根本不可能的(使用ActiveXObject访问本地文件系统并不算使用ActiveX只会带来安全问题)。

这个方法也需要AngularJS 1.4。x或更高版本,但你可以调整它以使用$provide.decorator而不是angular.Module.decorator -我写了这个要点来演示如何在符合John Papa的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;
};
}
})();

为什么一开始不这么做呢?AngularJS的支持只支持到IE9。如果你不同意这个决定,并认为他们无论如何都应该把它放进去,那就赶紧转向Angular 2+,因为更好的现代支持正是Angular 2存在的原因。

问题是(如前所述)没有文件api 支持这样做是不可行的,因为我们的核心 基线是IE9和填充这个东西是不可能的 核心。< / p >

此外,试图以一种不是的方式处理此输入 跨浏览器兼容只会让第三方解决方案更加困难, 现在必须对抗/禁用/解决核心方案。

...

我要像关闭#1236一样关闭它。Angular 2 构建以支持现代浏览器和该文件支持将 容易。< / p >

或者你可以获取输入并设置onchange函数:

<input type="file" id="myFileInput" />
document.getElementById("myFileInput").onchange = function (event) {
console.log(event.target.files);
};