如何使用 ng-model 双向绑定到 type="file" 输入?

Posted

技术标签:

【中文标题】如何使用 ng-model 双向绑定到 type="file" 输入?【英文标题】:How do I two-way bind to a type="file" input with ng-model? 【发布时间】:2017-01-11 19:01:34 【问题描述】:

基本上我想这样做:

<input type="file" ng-model="variable_in_scope">

当我选择一个文件时,variable_in_scope 应该被分配给选择的文件对象。此外,如果 variable_in_scope 的值在我页面的其他任何地方发生更改,它应该更新“选择文件”按钮旁边的文本以指示所选文件已更改。

对于任何其他类型的输入,这都行得通。

我不需要做任何花哨的事情,比如实际查看文件的内容。最终,我想发布它,但我发现你可以通过setting the file object you get into a FormData object 做到这一点,而无需实际将内容读入 javascript-land。

我发现了其他关于使用 angular 选择文件的问题,但没有一个有双向绑定解决方案。

【问题讨论】:

ng-model for <input type="file"/>的可能重复 【参考方案1】:

My answer on another question 提供了一种使用ng-model 执行此操作的方法,但由于该问题并非专门关于双向绑定(而且我的答案在那里很难找到),我将在此处重现它:

app.directive('bindFile', [function () 
    return 
        require: "ngModel",
        restrict: 'A',
        link: function ($scope, el, attrs, ngModel) 
            el.bind('change', function (event) 
                ngModel.$setViewValue(event.target.files[0]);
                $scope.$apply();
            );

            $scope.$watch(function () 
                return ngModel.$viewValue;
            , function (value) 
                if (!value) 
                    el.val("");
                
            );
        
    ;
]);

Demo

要使用它,您只需将其添加到您的 Angular 模块中,并在要使用它的文件选择器中包含 bind-file 属性。

【讨论】:

【参考方案2】:

Angular doesn't support binding to file-type inputs,但我使用许多其他答案拼凑出一个解决方案。

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 向我展示了如何使用指令来实现与 ng-model 的自定义绑定。它可以访问文件的内容,因此如果您需要该功能,可以将其添加回我的解决方案中。

但是,它的绑定存在一些问题。它会正确设置我的variable_in_scope 的值,但如果有其他东西绑定到variable_in_scope 的值,它们就不会更新。 The trick was to use isolate scope and $apply. 那你就不用搞这个$setViewValue 业务了。只需设置它并忘记它。

这让我达到了单向绑定。但是,如果我将值设置为variable_in_scope,文件选择器仍然显示我选择了原始文件。就我而言,我真正想做的就是清除所选文件。我发现了the Javascript magic to do this,并在ngModel上设置了一个$watch来触发它。

如果您想以编程方式将文件设置为不同的值,祝您好运,因为FileList is read-only。 magic trick 可让您清除 FileList,但您无法添加任何内容。也许您可以创建一个新的FileList 并将其分配给.files,但粗略一瞥我并没有看到这样做的方法。

【讨论】:

【参考方案3】:

帮助我:FileUploader

用 display: none 隐藏输入元素;然后使用标签放在输入元素的顶部,并使用标签连接到范围变量。请参阅 SO 上的许多帖子。像这样:

<label for="idFileUpload" class="custom-file-upload">
    SelectFile
</label>
<input type="file" id="idFileUpload" nv-file-select uploader="uploader">
<label id="idSelectedFile">selectedFile</label>
<md-button id="idUploadBtn" md-no-ink class="md-primary settingsBtns" ng-click="uploader.uploadAll()" ng-disabled="!uploader.getNotUploadedItems().length">Upload</md-button>

CSS:

input[type="file"] 
    display: none;


.custom-file-upload 
    background-color: green;
    color: white;
    border: 1px solid #ccc;
    display: inline-block;
    padding: 6px 12px;
    cursor: pointer;

在 FileUploader 触发的事件中,您可以获取输入元素的值。高温

【讨论】:

以上是关于如何使用 ng-model 双向绑定到 type="file" 输入?的主要内容,如果未能解决你的问题,请参考以下文章

玩转angularJs——通过自定义ng-model,不仅仅只是input可以有双向绑定

ng-model 指令

ng-model

AngularJS学习之旅—AngularJS 模型

angularjs的数据双向绑定怎么实现的?

ng-model 绑定到 ng-repeat Angularjs 中的元素