When encountering the error message 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.
, it indicates that there's an issue with setting the value property of an HTML input element.
To address this error, follow these steps:
""
).In your component's HTML file, add an event listener to listen for changes:
<input type="file" (change)="onFileChange($event)" #file formControlName="imageName">
In your component's TypeScript file, the following function will create a new file with the updated file name:
onFileChange(event) {
//console.log(event.target.files[0].name)
var blob = event.target.files[0].slice(0, event.target.files[0].size, 'image/png');
const newFile = new File([blob], this.dat.imageName, {type: 'image/png'})
//console.log(newFile)
}
This preserves the original file name on input, but you can utilize newFile
in your POST/PUT request to be sent to the backend.
As the error suggests, setting anything other than an empty string to a file input's value could pose security risks, hence why the code likely never worked. It might have functioned in some non-standard (poor) browsers, but it's not recommended.
Removing the line should theoretically work, as setting the same value to the input that it already has seems unnecessary.
However, it appears that a custom ValueAccessor is required for validating file inputs, as indicated in another answer.
Angular lacks a ControlValueAccessor for file inputs. This means it defaults to the DefaultValueAccessor, which attempts to write the value of the avatar to the control. However, this is not permitted for a FileInputControl.
You have a couple of options:
uploadImage
function into the ControlValueAccessor.formControlName="avatar"
and refrain from using reactive forms since you're handling the events manually anyway.Here's an example of a custom ControlValueAccessor:
@Directive({
selector: 'input[type=file][formControlName]',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: FileUploadAccessor,
multi: true
}
]
})
export class FileUploadAccessor implements ControlValueAccessor {
private onChange: Function;
@HostListener('change', ['$event.target.files']) emitFiles( event: FileList ) {
const file = event && event.item(0);
this.onChange(file);
}
constructor( private host: ElementRef<HTMLInputElement> ) {
}
writeValue( value: null ) {
// clear file input
this.host.nativeElement.value = '';
}
registerOnChange( fn: Function ) {
this.onChange = fn;
}
registerOnTouched( fn: Function ) {
}
}
You can also add your current processing to this directive instead of calling this.onChange(file);
. For example:
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
this.onChange(reader.result as string);
}