ngx-dropzone
ngx-dropzone copied to clipboard
Handle blur event for updateOn blur with reactive form
I implement ngx-dropzone correctly and everything is working fine. I switched my form from updateOn: 'submit' to updateOn: 'blur'.
Even though it's working, the behaviour is not the one intended:
- When I click on the dropzone, the blur is triggered and thus my custom error asking for at least 1 image is displayed. At this point, the file explorer is open and I haven't chose any picture.
- When the picture is selected the error do not disappear, so I need to click outside of the dropzone.
The second point could be understandable but the first one is not normal (I think).
To make the dropzone work with the updateOn: 'blur', I did the following:
- Add tabindex="0" on the ngx-dropzone
- Add (blur)="onBlur()" on the ngx-dropzone
- Get the Viewchild of the dropzone in the TS
- When I select a picture call this: this.dropzone?.nativeElement.blur()
Unfortunately, the problem remain same. In fact it's worse because when I click outside of the dropzone, the error does not disappear.
My question is: Is there a way to handle correctly the blur event of the dropzone ? (when I click on it => the blur event is not triggered and when I select a picture => the blur event is triggered)
My HTML:
<ngx-dropzone #dropzone (change)="onSelect($event)" (blur)="onBlur()" accept="image/jpeg,image/jpg,image/png" tabindex="0">
<ngx-dropzone-label *ngIf="files.length===0">
<app-svg name="cloud_upload"></app-svg>
<label>{{ 'common.dragndrop' | transloco }}</label>
</ngx-dropzone-label>
<custom-preview ngProjectAs="ngx-dropzone-preview" *ngFor="let f of files" [file]="f" [removable]="true" (removed)="onRemove(f)">
</custom-preview>
</ngx-dropzone>
My TS:
export class DropzoneComponent implements ControlValueAccessor {
@ViewChild('dropzone', { read: ElementRef }) dropzone: ElementRef | undefined = undefined
files: File[] = []
imgResultBeforeCompress: string = ''
imgResultAfterCompress: string = ''
imageBase64: string | ArrayBuffer | null | undefined = undefined
isFileSizeUnder4MB: boolean = false
onChange: any = () => { }
onTouch: any = () => { }
touched = false
disabled = false
constructor(private compressImage: CompressImageService) {}
onSelect(event: NgxDropzoneChangeEvent) {
if (this.disabled) return
// this.markAsTouched()
event.addedFiles.forEach(file => {
this.compressImage.compress(file, 0.5, (file: File) => {
this.checkIfFileSizeIsUnder4MB(file)
this.files.push(file)
})
})
this.dropzone?.nativeElement.blur()
this.onChange(this.files)
}
onRemove(event: File) {
if (this.disabled) return
// this.markAsTouched()
this.files.splice(this.files.indexOf(event), 1)
this.onChange(this.files)
}
checkIfFileSizeIsUnder4MB(file: File) {
if (file.size > 4000000) {
this.isFileSizeUnder4MB = false
} else {
this.isFileSizeUnder4MB = true
}
}
/* Methods needed by ControlValueAccessor to transform this component into a "form friendly" component */
registerOnChange(providedFunction: any) {
this.onChange = providedFunction
}
registerOnTouched(providedFunction: any) {
this.onTouch = providedFunction
}
writeValue(providedValue: any) {
if (providedValue) {
this.files = providedValue
this.files.forEach(file => this.checkIfFileSizeIsUnder4MB(file))
}
}
setDisabledState(providedDisabledVal: any) {
this.disabled = providedDisabledVal
}
onBlur() {
this.onTouch()
}
markAsTouched() {
if (!this.touched) {
this.onTouch()
this.touched = true
}
}
validate() {
return !this.isFileSizeUnder4MB && {
invalid: true,
}
}
}
Hi @william-fargues,
thank you for your feedback on using the dropzone within a form.
Unfortunately, the native <input type="file" /> element does not play very well together with Angular's form controls, and so does the dropzone.
The good news is that I am actually planning on improving the form control experience in the future by providing a wrapper directly with the component. Until then, maybe this article by Ben Nadel helps you.
Happy coding! 🤓