File Upload with Angular Material

183,592

Solution 1

I find a way to avoid styling my own choose file button.

Because I'm using flowjs for resumable upload, I'm able to use the "flow-btn" directive from ng-flow, which gives a choose file button with material design style.

Note that wrapping the input element inside a md-button won't work.

Solution 2

Nice solution by 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

View in codepen

Solution 3

For 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>

Component method:

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

Note: This filters to just allow .csv files.

Solution 4

Another example of the solution. Will look like the following enter image description here

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

Hope it helps!

Solution 5

Based on this answer. It took some time for me to make this approach working, so I hope my answer will save someone's time.

DEMO on CodePen

Directive:

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>
Share:
183,592
WreckingBall
Author by

WreckingBall

Updated on June 17, 2021

Comments

  • WreckingBall
    WreckingBall almost 3 years

    I'm writing an web app with AngularJS and angular-material. The problem is that there's no built-in component for file input in angular-material. (I feel that file uploading doesn't fit the material design, but I need it in my app)

    Do you have a good solution for this problem?

  • Leo Caseiro
    Leo Caseiro about 8 years
    That's the easiest way. However, I'd switch the md-button for a label for="inputid", so you don't need to bind your click. The Label does but itself. Eq. <input class="ng-hide" id="FileUploadInput" multiple type="file" /> <label for="FileUploadInput" class="md-button md-raised md-primary">Choose Files</label>
  • krico
    krico about 8 years
    Rather than using "disabled" on the input for "fileName" I used ng-readonly="true". But the idea in itself is the best!
  • Lucas Kuhlemann
    Lucas Kuhlemann over 7 years
    @LeoCaseiro Care to put that in an answer?
  • Guy Schalnat
    Guy Schalnat almost 7 years
    However, if you want to actually give the user some feedback on the file they selected (like the <input type="file"> does), then look at other solutions.
  • Guy Schalnat
    Guy Schalnat almost 7 years
    Also, the label doesn't behave correctly with the tab and enter keys, even if you put tabindex="0" on it.
  • Guy Schalnat
    Guy Schalnat almost 7 years
    At least in 2017, the label doesn't seem to behave correctly using the tab / enter key combinations, while the button does.
  • Mendy
    Mendy over 4 years
    Can you explain why onclick="this.value=null" is necessary?
  • rynop
    rynop over 4 years
    It forces a change event. Ex: you choose me.jpg and your Angular app displays a preview client side. You then notice your me.jpg isn't cropped correctly, so you modify me.jpg in photoshop, then go back to your webapp, click the button and choose me.jpg again. Without setting this.value=null your logic to re-render the image on the page would never fire. So not necessary, but it is good practice IMO.
  • abhigyan nayak
    abhigyan nayak over 4 years
    Nice try..But, I feel it does'nt support Multiple files
  • David Buck
    David Buck about 4 years
    While this code may solve the question, including an explanation of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please edit your answer to add explanations and give an indication of what limitations and assumptions apply.
  • Sathiamoorthy
    Sathiamoorthy almost 3 years
    Perfect example. Thank you
  • Patrick Prakash
    Patrick Prakash over 2 years
    Some edit suggestions this.selectedFiles = event.target.files will not work directly in angular now. Use this.selectedFiles = (event.target as HTMLInputElement).files;