I'm parsing excel sheets but If there are more than just 1 sheet the user needs to choose. I want to do this with a dialog and the function needs to wait until the result is there.
My code:
app.component.ts:
onFileSelected(file: File): void {
const reader: FileReader = new FileReader();
reader.onload = async (e: any) => {
// read workbook
const bstr: string = e.target.result;
const wb: XLSX.WorkBook = XLSX.read(bstr, {
type: "binary",
sheetRows: 101
});
this.sheetNames = wb.SheetNames;
if (this.sheetNames.length > 1) {
console.log("größer eins");
await this.openDialog();
}
// grab first sheet
const wsname: string = wb.SheetNames[this.sheetIndex];
const ws: XLSX.WorkSheet = wb.Sheets[wsname];
this.data = XLSX.utils.sheet_to_json(ws, { header: 0 });
};
reader.readAsBinaryString(this.uploadComponent.file);
}
openDialog(): void {
const dialogRef = this.chooseSheetDialog.open(ChooseSheetDialogComponent, {
width: "500px",
data: { sheets: this.sheetNames }
});
dialogRef.afterClosed().subscribe(result => {
console.log("The dialog was closed " + result);
this.sheetIndex = result;
});
}
It's a shortened form.
The dialog.ts:
export class ChooseSheetDialogComponent {
selectFormControl = new FormControl("", Validators.required);
constructor(
@Inject(MAT_DIALOG_DATA) private data: any,
public dialogRef: MatDialogRef<ChooseSheetDialogComponent>
) {}
onNoClick(): void {
this.dialogRef.close();
}
}
dialog.html:
<h1 mat-dialog-title>Wähle aus</h1>
<div mat-dialog-content>
<p>Es existieren mehrere Arbeitsblätter / Tabellen. Bitte wähle.</p>
<mat-form-field>
<mat-label>Favorite sheet</mat-label>
<mat-select required [formControl]="selectFormControl">
<mat-option *ngFor="let sheet of data.sheets" [value]="sheet">
{{sheet}}
</mat-option>
</mat-select>
<mat-error *ngIf="selectFormControl.hasError('required')">
This field is required
</mat-error>
</mat-form-field>
</div>
<div mat-dialog-actions>
<button mat-button (click)="onNoClick()">No Thanks</button>
<button mat-button [mat-dialog-close]="selectFormControl.value" cdkFocusInitial>Ok</button>
</div>
But yes however await this.openDialog();
does not work. Error in the browser:
ERROR Error: Uncaught (in promise): TypeError: Cannot read property '!ref' of undefined
TypeError: Cannot read property '!ref' of undefined
at AppComponent.getExcelHeaderRow (app.component.ts:121)
You can use toPromise
function in order to have a Promise-oriented logic. Also, the function must return a Promise
so that you can use await
later:
async openDialog(): Promise<number> {
const dialogRef = this.chooseSheetDialog.open(ChooseSheetDialogComponent, {
width: "500px",
data: { sheets: this.sheetNames }
});
return dialogRef.afterClosed()
.toPromise() // here you have a Promise instead an Observable
.then(result => {
console.log("The dialog was closed " + result);
this.sheetIndex = result;
return Promise.resolve(result); // will return a Promise here
});
}
Then, your onFileSelected
function can use await
as follows:
async onFileSelected(file: File): void {
const result = await this.openDialog(); // waiting here
console.log('result', result); // you got the value
}
Also, I wrote a working sample here. Don't forget to open the browser's console to see the results.
I hope it helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With