【问题标题】:File Upload with Angular Material使用 Angular 材料上传文件
【发布时间】:2015-10-30 06:38:17
【问题描述】:

我正在使用 AngularJS 和 angular-material 编写一个 Web 应用程序。问题是角度材料中没有用于文件输入的内置组件。 (我觉得文件上传不适合material design,但我的app里需要)

你有解决这个问题的好办法吗?

【问题讨论】:

  • 输入正常工作只需将类型设置为文件:<input type=file>。在输入容器中添加此代码。
  • 我最近发现了这个:https://github.com/shuyu/angular-material-fileinput 它很合身,就像一个魅力。文档简单直接地说明了如何使用它。
  • 两年后,<input type="file"> 确实允许用户选择一个文件,但按钮是纯 html 外观,而不是 Material 风格,这对我来说根本不起作用。

标签: javascript html angularjs file-upload angular-material


【解决方案1】:

leocaseiro 提供的不错的解决方案

<input class="ng-hide" id="input-file-id" multiple type="file" />
<label for="input-file-id" class="md-button md-raised md-primary">Choose Files</label>

codepen查看

【讨论】:

  • 但是,如果您想实际向用户提供有关他们选择的文件的一些反馈(如 &lt;input type="file"&gt; 所做的那样),请查看其他解决方案。
  • 此外,即使您将 tabindex="0" 放在标签上,标签也无法正确使用选项卡和输入键。
【解决方案2】:

对于 Angular 6+:

HTML:

<input #csvInput hidden="true" type="file" onclick="this.value=null" (change)="csvInputChange($event)" accept=".csv"/>
<button mat-flat-button color="primary" (click)="csvInput.click()">Choose Spreadsheet File (CSV)</button>

组件方法:

  csvInputChange(fileInputEvent: any) {
    console.log(fileInputEvent.target.files[0]);
  }

注意:此过滤器只允许.csv 文件。

【讨论】:

  • 你能解释一下为什么onclick="this.value=null" 是必要的吗?
  • 它强制一个change事件。例如:您选择me.jpg,您的 Angular 应用程序会显示预览客户端。然后您注意到您的me.jpg 没有正确裁剪,因此您在photoshop 中修改me.jpg,然后返回您的Web 应用程序,单击按钮并再次选择me.jpg。如果不设置this.value=null,您在页面上重新渲染图像的逻辑将永远不会触发。所以没有必要,但这是 IMO 的好习惯。
【解决方案3】:

解决方案的另一个例子。 将如下所示

CodePen 链接there

  <choose-file layout="row">
    <input id="fileInput" type="file" class="ng-hide">
    <md-input-container flex class="md-block">
      <input type="text" ng-model="fileName" disabled>
      <div class="hint">Select your file</div>
    </md-input-container>
    <div>
      <md-button id="uploadButton" class="md-fab md-mini">
        <md-icon class="material-icons">attach_file</md-icon>
      </md-button>
    </div>
  </choose-file>   

.directive('chooseFile', function() {
    return {
      link: function (scope, elem, attrs) {
        var button = elem.find('button');
        var input = angular.element(elem[0].querySelector('input#fileInput'));

        button.bind('click', function() {
          input[0].click();
        });

        input.bind('change', function(e) {
          scope.$apply(function() {
            var files = e.target.files;
            if (files[0]) {
              scope.fileName = files[0].name;
            } else {
              scope.fileName = null;
            }
          });
        });
      }
    };
  });

希望对你有帮助!

【讨论】:

  • 我使用 ng-readonly="true",而不是在“fileName”的输入上使用“disabled”。但这个想法本身就是最好的!
【解决方案4】:

基于this answer。我花了一些时间才使这种方法起作用,所以我希望我的回答能节省一些人的时间。

DEMO on CodePen

指令:

angular.module('app').directive('apsUploadFile', apsUploadFile);

function apsUploadFile() {
    var directive = {
        restrict: 'E',
        templateUrl: 'upload.file.template.html',
        link: apsUploadFileLink
    };
    return directive;
}

function apsUploadFileLink(scope, element, attrs) {
    var input = $(element[0].querySelector('#fileInput'));
    var button = $(element[0].querySelector('#uploadButton'));
    var textInput = $(element[0].querySelector('#textInput'));

    if (input.length && button.length && textInput.length) {
        button.click(function (e) {
            input.click();
        });
        textInput.click(function (e) {
            input.click();
        });
    }

    input.on('change', function (e) {
        var files = e.target.files;
        if (files[0]) {
            scope.fileName = files[0].name;
        } else {
            scope.fileName = null;
        }
        scope.$apply();
    });
}

upload.file.template.html

<input id="fileInput" type="file" class="ng-hide">
<md-button id="uploadButton"
           class="md-raised md-primary"
           aria-label="attach_file">
    Choose file
</md-button>
<md-input-container md-no-float>
    <input id="textInput" ng-model="fileName" type="text" placeholder="No file chosen" ng-readonly="true">
</md-input-container>

【讨论】:

  • 不错的尝试..但是,我觉得它不支持多个文件
【解决方案5】:

来自https://github.com/angular/material/issues/3310@jameswyse

HTML

<input id="fileInput" name="file" type="file" class="ng-hide" multiple>
<md-button id="uploadButton" class="md-raised md-primary"> Choose Files </md-button>

控制器

    var link = function (scope, element, attrs) {
    const input = element.find('#fileInput');
    const button = element.find('#uploadButton');

    if (input.length && button.length) {
        button.click((e) => input.click());
    }
}

为我工作。

【讨论】:

  • 这是最简单的方法。但是,我会将 md 按钮切换为“inputid”标签,因此您无需绑定单击。 Label 只做它自己。方程。
  • @LeoCaseiro 想把它放在答案中吗?
  • 至少在 2017 年,标签使用 tab / enter 组合键的行为似乎不正确,而按钮却可以。
【解决方案6】:

我找到了一种方法来避免设置我自己的选择文件按钮的样式。

因为我使用 flowjs 进行可恢复上传,我可以使用 ng-flow中的“flow-btn”指令>,它提供了一个具有材料设计风格的选择文件按钮。

请注意,将输入元素包装在 md 按钮内是行不通的。

【讨论】:

    【解决方案7】:

    我已经加入了此处发布的一些信息以及使用 Angular Material 个性化组件的可能性,这是我在没有外部库的情况下的贡献,并且将所选文件的名称反馈到字段中:

    HTML

    <mat-form-field class="columns">
        <mat-label *ngIf="selectedFiles; else newFile">{{selectedFiles.item(0).name}}</mat-label>
        <ng-template #newFile>
            <mat-label>Choose file</mat-label>
        </ng-template>
        <input matInput disabled>
        <button mat-icon-button matSuffix (click)="fileInput.click()">
            <mat-icon>attach_file</mat-icon>
        </button>
        <input hidden (change)="selectFile($event)" #fileInput type="file" id="file">
    </mat-form-field>
    

    TS

    selectFile(event) {
        this.selectedFiles = event.target.files;
    }
    

    【讨论】:

    • 一些编辑建议 this.selectedFiles = event.target.files 现在不能直接在 Angular 中工作。使用 this.selectedFiles = (event.target as HTMLInputElement).files;
    【解决方案8】:

    使用 Angular 材料
    HTML

    <div (click)="uploadFile.click()">
       <button mat-raised-button color="primary">Choose File</button>
       <input #uploadFile (change)="upload($event)" type='file' style="display:none"/> 
    </div>
    

    ts

    upload(event:Event){
       console.log(event)
    }
    

    stackblitz

    【讨论】:

    • 完美的例子。谢谢
    【解决方案9】:

    您可以通过将输入包装在标签内来更改样式并将输入显示更改为无。然后,您可以指定要在 span 元素中显示的文本。 注意:这里我使用了 bootstrap 4 按钮样式(btn btn-outline-primary)。你可以使用任何你想要的风格。

    <label class="btn btn-outline-primary">
          <span>Select File</span>
          <input type="file">
    </label>
    
    input {
      display: none;
    }
    

    【讨论】:

      【解决方案10】:

      另一个被黑的解决方案,虽然通过实现一个代理按钮可能会更干净一些:

      HTML:

      <input id="fileInput" type="file">
      <md-button class="md-raised" ng-click="upload()">
        <label>AwesomeButtonName</label>
      </md-button>
      

      JS:

      app.controller('NiceCtrl', function ( $scope) {
        $scope.upload = function () {
          angular.element(document.querySelector('#fileInput')).click();
        };
      };
      

      【讨论】:

        【解决方案11】:
        html:
            <div class="upload">
                <span>upload image</span>
                <input
                  #Image
                  type="file"
                  (change)="handleFileInput($event.target.files)"
                  accept=".jpg,.svg,.png,.jpeg"
                />
                <img
                  width="100%"
                  height="100%"
                  *ngIf="imageUrl"
                  [src]="imageUrl"
                  class="image"
                />
            </div>
        

        app.component.ts

        export class AppComponent {
          options = [{ value: "This is value 1", checked: true }];
          statuses = ["control"];
        
          // name = "Angular";//
          fileToUpload: any;
          imageUrl: any;
          handleFileInput(file: FileList) {
            this.fileToUpload = file.item(0);
        
            //Show image preview
            let reader = new FileReader();
            reader.onload = (event: any) => {
              this.imageUrl = event.target.result;
            };
            reader.readAsDataURL(this.fileToUpload);
          }
        }
        

        【讨论】:

        • 虽然此代码可能会解决问题,including an explanation 关于如何以及为什么解决问题将真正有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提问的人。请编辑您的答案以添加解释并说明适用的限制和假设。
        【解决方案12】:

        除了以上所有答案(这就是我将其设为社区 wiki 的原因),最好用tabindex="-1" 标记任何input&lt;type="text"&gt;,尤其是在使用只读而不是禁用时(也许&lt;input type="file"&gt; ,虽然它应该被隐藏,但显然它仍然在文档中)。使用 tab/enter 组合键时,标签无法正确操作,但按钮可以。因此,如果您要复制此页面上的其他解决方案之一,您可能需要进行这些更改。

        【讨论】:

          【解决方案13】:

          带有 AngularJs Material 和 mime 类型验证的文件上传器:

          指令:

          function apsUploadFile() {
              var directive = {
                  restrict: 'E',
                  require:['ngModel', 'apsUploadFile'],
                  transclude: true,
                  scope: {
                      label: '@',
                      mimeType: '@',
                  },
                  templateUrl: '/build/html/aps-file-upload.html',
                  controllerAs: 'ctrl',
                  controller: function($scope) {
                      var self = this;
          
                      this.model = null;
          
                      this.setModel = function(ngModel) {
                          this.$error = ngModel.$error;
          
                          ngModel.$render = function() {
                              self.model = ngModel.$viewValue;
                          };
          
                          $scope.$watch('ctrl.model', function(newval) {
                              ngModel.$setViewValue(newval);
                          });
                      };
                  },
                  link: apsUploadFileLink
              };
              return directive;
          }
          
          function apsUploadFileLink(scope, element, attrs, controllers) {
          
              var ngModelCtrl = controllers[0];
              var apsUploadFile = controllers[1];
          
              apsUploadFile.inputname = attrs.name;
              apsUploadFile.setModel(ngModelCtrl);
          
              var reg;
              attrs.$observe('mimeType', function(value) {
                  var accept = value.replace(/,/g,'|');
                  reg = new RegExp(accept, "i");
                  ngModelCtrl.$validate();
              });
          
              ngModelCtrl.$validators.mimetype = function(modelValue, viewValue) {
          
                  if(modelValue.data == null){
                      return apsUploadFile.valid = true;
                  }
          
                  if(modelValue.type.match(reg)){
                      return apsUploadFile.valid = true;
                  }else{
                      return apsUploadFile.valid = false;
                  }
          
              };
          
              var input = $(element[0].querySelector('#fileInput'));
              var button = $(element[0].querySelector('#uploadButton'));
              var textInput = $(element[0].querySelector('#textInput'));
          
              if (input.length && button.length && textInput.length) {
                  button.click(function(e) {
                      input.click();
                  });
                  textInput.click(function(e) {
                      input.click();
                  });
              }
          
              input.on('change', function(e) {
          
                  //scope.fileLoaded(e);
                  var files = e.target.files;
          
                  if (files[0]) {
                      ngModelCtrl.$viewValue.filename = scope.filename = files[0].name;
                      ngModelCtrl.$viewValue.type = files[0].type;
                      ngModelCtrl.$viewValue.size = files[0].size;
          
                      var fileReader = new FileReader();
                      fileReader.onload = function () {
                          ngModelCtrl.$viewValue.data = fileReader.result;
                          ngModelCtrl.$validate();
                      };
                      fileReader.readAsDataURL(files[0]);
          
                      ngModelCtrl.$render();
                  } else {
                      ngModelCtrl.$viewValue = null;
                  }
          
                  scope.$apply();
              });
          
          }
          app.directive('apsUploadFile', apsUploadFile);
          

          html模板:

          <input id="fileInput" type="file" name="ctrl.inputname" class="ng-hide">
          <md-input-container md-is-error="!ctrl.valid">
              <label>{@{label}@}</label>
              <input id="textInput" ng-model="ctrl.model.filename" type="text" ng-readonly="true">
              <div ng-messages="ctrl.$error" ng-transclude></div>
          </md-input-container>
          <md-button id="uploadButton" class="md-icon-button md-primary" aria-label="attach_file">
              <md-icon class="material-icons">cloud_upload</md-icon>
          </md-button>
          

          示例:

          <div layout-gt-sm="row">
              <aps-upload-file name="strip" ng-model="cardDesign.strip" label="Strip" mime-type="image/png" class="md-block">
                  <div ng-message="mimetype" class="md-input-message-animation ng-scope" style="opacity: 1; margin-top: 0px;">Your image must be PNG.</div>
              </aps-upload-file>
          </div>
          

          【讨论】:

            猜你喜欢
            • 2017-05-02
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-11-22
            • 1970-01-01
            • 2016-03-30
            • 2020-03-04
            • 2016-10-24
            相关资源
            最近更新 更多