使用Angular Material进行文件上传

75

我正在使用AngularJS和angular-material编写一个Web应用程序。问题是angular-material没有内置的文件输入组件。(我觉得文件上传不符合Material Design,但是我需要它在我的应用程序中)

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


输入正常工作,只需将类型设置为文件:<input type=file>。将此代码添加到输入容器中。 - govindpatel
1
我最近发现了这个:https://github.com/shuyu/angular-material-fileinput 它非常适合,并且运行得很好。文档简单明了,说明了如何使用它。 - leocborges
2
两年后,<input type="file"> 确实允许用户选择文件,但按钮只是普通的 HTML 样式,而不是 Material 风格的,这对我来说一点也不好。 - Guy Schalnat
13个回答

61

对于 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文件。


1
你能解释一下为什么需要 onclick="this.value=null" 吗? - Mendy
7
它会强制触发一个"change"事件。例如,你选择了"me.jpg",然后你的Angular应用程序在客户端显示一个预览。然后你注意到"me.jpg"没有正确裁剪,所以你在Photoshop中修改了"me.jpg",然后回到你的Web应用程序,再次点击按钮并选择了"me.jpg"。如果不设置"this.value=null",你重新渲染页面上的图片的逻辑将永远不会触发。因此这不是必须的,但在我看来这是个好习惯。 - rynop

57

leocaseiro的解决方案不错,Github链接

<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中查看


8
然而,如果你想要向用户提供一些有关他们所选择的文件的反馈(就像<input type="file">所做的那样),则需要寻找其他解决方案。 - Guy Schalnat
2
另外,即使你在标签上添加了tabindex="0",标签也无法正确响应Tab和Enter按键。 - Guy Schalnat

15

解决方案的另一个示例。 将看起来像下面这样: enter image description here

CodePen链接在这里

  <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;
            }
          });
        });
      }
    };
  });

希望能对你有所帮助!


1
与其在“fileName”的输入上使用“disabled”,我使用了ng-readonly =“true”。但是这个想法本身是最好的! - krico

13

根据这个答案。我花了一些时间让这种方法工作,所以我希望我的答案能够节省别人的时间。

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();
    });
}

上传文件模板.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>

不错的尝试..但是我感觉它不支持多个文件。 - abhigyan nayak

12

来自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());
    }
}

对我有用。


5
这是最简单的方法。不过,我会将md-button替换为label for="inputid",这样您就不需要绑定点击事件了。Label本身就能够实现。等效于: <input class="ng-hide" id="FileUploadInput" multiple type="file" /> <label for="FileUploadInput" class="md-button md-raised md-primary">选择文件</label> - Leo Caseiro
@LeoCaseiro 能否把它写成答案? - Lucas Kuhlemann
至少在2017年,使用Tab / Enter键组合时,标签似乎无法正确地工作,而按钮可以。 - Guy Schalnat

10

使用 Angular Material
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


1
完美的例子。谢谢。 - Sathiamoorthy

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;
}

一些编辑建议: 在Angular中,直接使用this.selectedFiles = event.target.files将无法正常工作。 请改用this.selectedFiles = (event.target as HTMLInputElement).files; - Patrick Prakash

6

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

因为我正在使用flowjs进行可恢复上传,所以我可以使用来自ng-flow的"flow-btn"指令,它提供了一个带有材料设计风格的选择文件按钮。

请注意,将输入元素包装在 md-button 内将无效。


3
注意:此处我使用了Bootstrap 4的按钮样式(btn btn-outline-primary),你可以使用任何样式。
<label class="btn btn-outline-primary">
      <span>Select File</span>
      <input type="file">
</label>

input {
  display: none;
}

3
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);
  }
}

2
虽然这段代码可能解决了问题,但是包括解释它如何以及为什么解决了问题,将有助于提高您的帖子质量,并可能导致更多的赞。请记住,您正在回答未来读者的问题,而不仅仅是现在提问的人。请编辑您的答案以添加解释,并指出适用的限制和假设。 - David Buck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接