使用Angular材料上传文件

问题描述 投票:42回答:10

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

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

javascript html angularjs file-upload angular-material
10个回答
43
投票

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>

enter image description here

查看在codepen


0
投票

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

13
投票

基于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>

13
投票

另一个解决方案的例子。看起来像下面的enter image description here

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

希望能帮助到你!


12
投票

来自https://github.com/angular/material/issues/3310的詹姆斯维斯

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>

CONTROLLER

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

为我工作。


7
投票

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


6
投票

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

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

请注意,将输入元素包装在md按钮内将不起作用。


2
投票

另一个被黑客攻击的解决方案,但通过实现代理按钮可能会更清洁:

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

1
投票

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

<label class="btn btn-outline-primary">
      <span>Select File</span>
      <input type="file">
</label>

input {
  display: none;
}

0
投票

添加上面的所有答案(这就是为什么我把它作为一个社区维基),最好用input<type="text">标记任何tabindex="-1",特别是如果使用readonly而不是disabled(也许是<input type="file">,虽然它应该隐藏,它是显然仍在文件中。使用制表符/输入键组合时标签无法正常操作,但按钮确实没有。因此,如果您要复制此页面上的其他解决方案之一,您可能希望进行这些更改。

© www.soinside.com 2019 - 2024. All rights reserved.