Angular 5 file upload: Failed to set the 'value' property on 'HTMLInputElement'
Asked Answered
K

6

32

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

Kizzie answered 22/4, 2018 at 21:36 Comment(0)
A
20

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

Aneroid answered 22/4, 2018 at 21:55 Comment(3)
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?Kizzie
You don't need to manually set the value of inputs for validators to work..Aneroid
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: #41889884Aneroid
A
35

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();
      };
    }
  }
Aminta answered 9/7, 2019 at 14:49 Comment(3)
I wonder why formcontrolname cant go on it?Lavinia
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.Nourish
But what when i have to use same senario and also patch value from API?Sprinkler
A
20

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

Aneroid answered 22/4, 2018 at 21:55 Comment(3)
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?Kizzie
You don't need to manually set the value of inputs for validators to work..Aneroid
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: #41889884Aneroid
H
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);
}
Hyperkinesia answered 26/8, 2020 at 11:13 Comment(1)
Removing "formControlName" from element solved my issue. Thanks!Illuminance
X
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.');
      })
  }
Xeric answered 2/12, 2020 at 20:42 Comment(0)
L
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.

Langouste answered 19/4, 2019 at 20:27 Comment(0)
O
0

Remove this:

formControlName="content"

<input #theFile type="file" (change)="onFileChange($event)" accept=".png" class="form-control"/> 
Oar answered 6/6 at 6:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.