31

I have a form for uploading a file in an angular 5 app, and as I have copied it exactly from a code I had written a while ago, I can swear it had worked before.

Here is my HTML code:

<form [formGroup]="form" (ngSubmit)="onSubmit()">
        <div class="form-group">
            <label>File:</label>
            <input #theFile type="file" (change)="onFileChange($event)" accept=".png" class="form-control" 
                    formControlName="content" />
            <input type="hidden" name="fileHidden" formControlName="imageInput"/>

                    <!-- [(ngModel)]="model.content" -->
            
            <div class="alert alert-danger" *ngIf="!form.prestine && form.controls.content.errors?.noFile">
                Please provide a photo.
            </div>
            <div class="alert alert-danger" *ngIf="form.controls.content.errors?.fileTooBig">
                The file is too big and won't uploaded. Maximum allowed size is 500kb.
            </div>
        </div>
        <div class="form-group">
            <label>Notes</label>
            <textarea type="text" class="form-control" formControlName="notes" [(ngModel)]="model.notes" > </textarea>
        </div>
        <button type="submit" class="btn btn-primary" [disabled]="!form.valid">Submit</button>
        <button class="btn btn-default" type="button" (click)="close(false);">Cancel</button>
    </form>

Here is the "onFileChange" method used in the fileUpload control:

onFileChange($event)
  {
    if ($event.target.files.length > 0)
    {
        let ftu: File = null;
        ftu = $event.target.files[0];
        this.form.controls['content'].setValue(ftu);
        this.model.content = $event.target.files[0];
    }
  }

and here is the code for the custom validator I have written and used:

import { FormControl } from '@angular/forms';

export class SekaniRootImageValidators
{
    static sizeTooBig(control: FormControl)
    {
        if (!control.value)
        {
            return { noFile : true }
        }
        else  if (control.value[0].size > 505000)
        {
            return { fileTooBig: true}
        }
        return null;

    }
}

Now the issue is as soon as I select a file in the input control, I get this error message in the console:

ERROR DOMException: Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string.

This code has worked before, so I have no idea where to even start. Any help is appreciated!

ATTENTION: Here is a link to a working answer: Angular2: validation for <input type="file"/> won't trigger when changing the file to upload

BinaryButterfly
  • 18,137
  • 13
  • 50
  • 91
Behrooz
  • 1,895
  • 4
  • 31
  • 47

5 Answers5

34

In my case I just removed the formControlName:

<input type="file" (change)="onFileChange($event)">

And .ts:

onFileChange(event) {
    const reader = new FileReader();

    if (event.target.files && event.target.files.length) {
      const [file] = event.target.files;
      reader.readAsDataURL(file);
      reader.onload = () => {
        this.data.parentForm.patchValue({
          tso: reader.result
        });

        // need to run CD since file load runs outside of zone
        this.cd.markForCheck();
      };
    }
  }
NicuVlad
  • 2,491
  • 3
  • 28
  • 47
  • 1
    I wonder why formcontrolname cant go on it? – Grant mitchell Jan 03 '21 at 23:15
  • 1
    If you have `formControlName` on it and you try to set the value programatically you get above mentioned error. So if you file input has required Validator in `FormGroup` and you try to test file upload, you fail because value won't be set and you are not able to set it. – Pauli Mar 16 '21 at 10:40
  • But what when i have to use same senario and also patch value from API? – Jignesh Panchal May 31 '22 at 05:25
18

Like the error is saying, you can only set an empty string to a file input value to clear the selection. It could open security risks otherwise. I can't imagine how that code could've ever worked. Maybe in some non-standard (bad) browser?

Shouldn't that code work if you just remove the line, why do you need to set the same value to the input that it already has anyway?

Edit: seems a custom ValueAccessor is needed for validating file inputs. Solution in another answer: Angular2: validation for <input type="file"/> won't trigger when changing the file to upload

funkizer
  • 4,626
  • 1
  • 18
  • 20
  • Because I want to use custom validators on that input. The validator will be attached to the form control object, how else would the validator know the value of the input control to be able to validate it? – Behrooz Apr 22 '18 at 21:58
  • You don't need to manually set the value of inputs for validators to work.. – funkizer Apr 22 '18 at 22:00
  • 3
    Googled a bit, looks like file inputs are not supported out of the box. You need to at least add a custom ValueAccessor. This answer might help: https://stackoverflow.com/questions/41889384/angular2-validation-for-input-type-file-wont-trigger-when-changing-the-fi/41938495#41938495 – funkizer Apr 23 '18 at 14:48
10

first of all you should remove "formControlName" from your element which type file, because it get's string then in your ts file you should add this

if (event.target.files && event.target.files.length > 0) {
  const file = (event.target.files[0] as File);
  this.yourForm.get('image').setValue(file);
  console.log(this.yourForm.get('image').value);
}
alireza zare
  • 133
  • 1
  • 6
5

Implement this by having 2 inputs for the file. Here is how I did it:

  this.myForm= this.formBuilder.group({
      fileSource: ['', Validators.required],
      fileName: '',
    })

HTML

<input type="file" formControlName='fileSource' (change)="onFileSelected($event)"/>

Typescript

  onFileSelected(event) {
    if(event.target.files.length > 0) 
     {
       this.myForm.patchValue({
          fileName: event.target.files[0],
       })
     }
  }

submit(){
    const formData = new FormData();
    formData.append('file', this.myForm.get('fileName').value);
   
    this.http.post('http://localhost:3000/upload', formData)
      .subscribe(res => {
        console.log(res);
        alert('Uploaded Successfully.');
      })
  }
James Ikubi
  • 2,552
  • 25
  • 18
2

Do not set the value of the input property to the selected file. Instead just set the file content into a variable and append to the request object separately. For the file input , just assign the even.target.value as the input value, so the user see the actual file selected.

voddy
  • 950
  • 1
  • 11
  • 21