Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cancelling file picker closes my stepper #3211

Closed
Tharrington86 opened this issue Oct 3, 2023 · 4 comments
Closed

Cancelling file picker closes my stepper #3211

Tharrington86 opened this issue Oct 3, 2023 · 4 comments

Comments

@Tharrington86
Copy link

Tharrington86 commented Oct 3, 2023

I am working on an Angular web app that collects data via a Nebular Stepper component. One of the steps allows users to attach images or documents using a file picker. Attaching an item works as expected but if you cancel the file picker, the underlying stepper dialog also closes and its data is lost. Am I correct to assume that this is being caused by the cancel event firing when closing the file picker? Is there a way to prevent that event from reaching the dialog?

stepper.component.ts

/**
   * Handler for the selectedFilesChanged event of mat-file-upload component
   * for Attachments.
   * @param {FileList} fileList List of File objects of the loaded files.
   */
  onSelectedAttachmentsChanged(fileList: FileList): void {
    // White list for attachments - just PDF for now.
    if (fileList.item(0).type == "application/pdf") {
      getBase64EncodedFromFile(fileList.item(0)).subscribe({
        next: (result: string) => {
          const newAttachment: Attachment = {
            filename: fileList.item(0).name,
            fileExtension: FileExtension.pdf,
            data: result,
            mimeType: fileList.item(0).type,
          };

          this.changeRequest.attachments = [
            ...this.changeRequest.attachments,
            newAttachment,
          ];
        },
        error: (err: Error) => {
          console.log("An error has occurred: ", err);
        },
      });
    } else {
      this._dialogService.open(ConfirmDialogComponent, {
        context: {
          confirmHeader: "Unsupported File Type",
          confirmMessage: "Only PDFs can be attached here.",
          confirmBtnText: "Ok",
          confirmBtnStatus: "basic",
          confirmStatus: "info",
          confirmShowCancelBtn: false,
        },
      });
    }
  }

stepper.component.html

<div fxLayout="row" fxLayoutAlign="center center" fxLayoutGap="1em">
          <mat-file-upload
            [acceptedTypes]="'.pdf'"
            [labelText]="''"
            [selectButtonText]="'Attach a PDF'"
            [showUploadButton]="false"
            [allowMultipleFiles]="false"
            (selectedFilesChanged)="onSelectedAttachmentsChanged($event)"
          ></mat-file-upload>
</div>

mat-file-upload.component.ts

import {
  Component,
  ElementRef,
  EventEmitter,
  Input,
  Output,
  ViewChild,
} from "@angular/core";

@Component({
  selector: "mat-file-upload",
  template: `
    <!--<span class="file-input-text">{{ labelText }}</span>-->
    <button
      nbButton
      [type]="selectFilesButtonType"
      [disabled]="disabled"
      color="primary"
      class="file-input-button"
      (click)="fileInput.click()"
      [attr.aria-label]="selectButtonText"
    >
      <nb-icon icon="plus-outline"></nb-icon>
      <span>{{ selectButtonText }}</span>
      <input
        #fileInput
        type="file"
        style="display: none"
        [accept]="acceptedTypes"
        [multiple]="allowMultipleFiles"
        (change)="filesChanged($event.target.files)"
      />
    </button>
    <button
      mat-raised-button
      [type]="uploadButtonType"
      color="primary"
      class="file-input-button"
      [disabled]="!selectedFiles || disabled"
      (click)="uploadFiles()"
      *ngIf="showUploadButton"
      [attr.aria-label]="uploadButtonText"
    >
      {{ uploadButtonText }}
    </button>
    <!--<span class="file-input-text">{{ selectedFileText }}</span>-->
    <!--<button
      mat-icon-button
      (click)="filesChanged(null)"
      type="button"
      aria-label="Remove Selected File(s)"
      *ngIf="selectedFiles != null && selectedFiles.length > 0"
    >
      <mat-icon *ngIf="!customSvgIcon">close</mat-icon>
      <mat-icon *ngIf="customSvgIcon" [svgIcon]="customSvgIcon"></mat-icon>
    </button>-->
  `,
  styles: [
    ".file-input-button { margin-right: 8px !important }",
    // eslint-disable-next-line max-len
    ".file-input-text { font-size: 14px !important; margin-right: 8px !important }",
  ],
})
export class MatFileUploadComponent {
  @Input() labelText = "Select File(s)";
  @Input() selectButtonText = "Select File(s)";
  @Input() selectFilesButtonType: "button" | "menu" | "reset" | "submit" =
    "button";
  @Input() uploadButtonText = "Upload File(s)";
  @Input() uploadButtonType: "button" | "menu" | "reset" | "submit" = "button";
  @Input() allowMultipleFiles = false;
  @Input() showUploadButton = true;
  @Input() acceptedTypes = "*.*";
  @Input() customSvgIcon?: string = null;
  @Input() disabled = false;
  @Output() uploadClicked: EventEmitter<FileList> = new EventEmitter<
    FileList
  >();
  @Output() selectedFilesChanged: EventEmitter<FileList> = new EventEmitter<
    FileList
  >();

  @ViewChild("fileInput") fileInputRef: ElementRef;
  selectedFiles: FileList;
  selectedFileText = "";

  filesChanged(files?: FileList): void {
    this.selectedFiles = files;
    this.selectedFilesChanged.emit(this.selectedFiles);
    if (this.selectedFiles) {
      const numSelectedFiles = this.selectedFiles.length;
      this.selectedFileText =
        numSelectedFiles === 1
          ? this.selectedFiles[0].name
          : `${numSelectedFiles} files selected`;
    } else {
      this.selectedFileText = "";
      this.resetFileInput();
    }
  }

  uploadFiles(): void {
    this.uploadClicked.emit(this.selectedFiles);
    this.resetFileInput();
  }

  resetFileInput(): void {
    this.fileInputRef.nativeElement.value = "";
  }
}
@JeevaRP-SDE
Copy link

@Tharrington86 ,
Can you please attach any Screenshot for this bug.
Like Select file click cancel.
What happened at very next second.

@Tharrington86
Copy link
Author

Tharrington86 commented Oct 9, 2023

@JeevaRP-SDE
Thank you for taking the time to assist. Here are some screen recordings showing what I am experiencing. I have also attached some code to the original post.

Cancelling the file picker closes the stepper and causes the entered data to be lost.
cancelNoSelect.webm

Cancelling the file picker after selecting and then deleting does not cause the stepper to close for one iteration, then closes stepper.
cancelAfterSelectAndDelete.webm

@Tharrington86
Copy link
Author

I confirmed that the file input is emitting a cancel event that is reaching my stepper component by adding an event listener to its constructor. Is there a way to make the stepper ignore that event?

@Tharrington86
Copy link
Author

Tharrington86 commented Oct 18, 2023

I was finally able to solve my problem by using HostListener to listen for the cancel event and stopping its propagation on my upload component.

@HostListener('cancel', ['$event'])
  onCancel(event: Event){ 
    console.log('Cancel host listener.'); 
    event.stopImmediatePropagation(); 
  };

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants