Skip to content
This repository has been archived by the owner on Aug 26, 2023. It is now read-only.

Handle blur event for updateOn blur with reactive form #127

Open
pepew-le-boss opened this issue Dec 2, 2021 · 1 comment
Open

Handle blur event for updateOn blur with reactive form #127

pepew-le-boss opened this issue Dec 2, 2021 · 1 comment
Labels
enhancement New feature or request

Comments

@pepew-le-boss
Copy link

pepew-le-boss commented Dec 2, 2021

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:

  1. 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.
  2. 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,
    }
  }

}
@peterfreeman
Copy link
Owner

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! 🤓

@peterfreeman peterfreeman added the enhancement New feature or request label Dec 19, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants