努力在 Angular 中的服务中使用带有异步 onload 方法的函数 - 相同的函数在组件中完美运行?
Stuggling to use function with async onload method with in a service in Angular - same function works perfect in component?
我在 Angular 中构建了一个组件,它导入一个 excel 文件并将其转换为一个数组,然后将 excel 文件中的内容作为 table。如果我按如下方式在组件中构建函数,这将非常有效:
数据-import.compoent.ts
import { Component, OnInit } from '@angular/core';
import { DataImporterService } from 'src/app/services/data-importer.service';
import * as excelhandler from 'xlsx';
@Component({
selector: 'app-data-import',
templateUrl: './data-import.component.html',
styleUrls: ['./data-import.component.css'],
})
export class DataImportComponent {
// Properties
importedExcelData: any;
constructor() {}
importFile (event: any){
const fileToUpload: DataTransfer = <DataTransfer>event.target;
if (fileToUpload.files.length !== 1)
throw new Error('Can not upload multiple files');
const fileReader: FileReader = new FileReader();
fileReader.onload = (e: any) => {
const binstring: string = e.target.result;
const workbook: excelhandler.WorkBook = excelhandler.read(binstring, {
type: 'binary',
});
const worksheetName: string = workbook.SheetNames[0];
const worksheet: excelhandler.WorkSheet = workbook.Sheets[worksheetName];
this.importedExcelData = excelhandler.utils.sheet_to_json(worksheet, {
header: 1,
});
};
fileReader.readAsBinaryString(fileToUpload.files[0])
}
数据-import.component.html
<input type="file" (change)="importFile($event)" multiple="false" />
<table>
<tbody>
<tr *ngFor="let row of importedExcelData">
<td style="margin-left:5em" *ngFor="let cell of row">{{ cell }}</td>
</tr>
</tbody>
</table>
问题是我正在努力使我的组件尽可能“仅供演示” - 所以自然的步骤是将这个 importFile
函数移到returns 一个可观察对象在组件中订阅的服务如下:
数据-importer.service.ts
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import {} from 'rxjs/add/observable/fromPromise';
import * as excelhandler from 'xlsx';
@Injectable({
providedIn: 'root',
})
export class DataImporterService {
// Properties
importedExcelData: any
// Functions
importFile(event: any): Observable<any> {
const fileToUpload: DataTransfer = <DataTransfer>event.target;
if (fileToUpload.files.length !== 1)
throw new Error('Can not upload multiple files');
const fileReader: FileReader = new FileReader();
fileReader.onload = (event: any) => {
const binstring: string = event.target.result;
const workbook: excelhandler.WorkBook = excelhandler.read(binstring, {
type: 'binary',
});
const worksheetName: string = workbook.SheetNames[0];
const worksheet: excelhandler.WorkSheet = workbook.Sheets[worksheetName];
this.importedExcelData = excelhandler.utils.sheet_to_json(worksheet, {
header: 1,
});
fileReader.readAsBinaryString(fileToUpload.files[0]);
};
return this.importedExcelData
}
}
修正数据-import.component.ts
import { Component} from '@angular/core';
import { DataImporterService } from 'src/app/services/data-importer.service';
import * as excelhandler from 'xlsx';
@Component({
selector: 'app-data-import',
templateUrl: './data-import.component.html',
styleUrls: ['./data-import.component.css'],
})
export class DataImportComponent {
// Properties
importedExcelData: any;
constructor(private dataService: DataImporterService) {}
importFile(event: any) {
this.dataService
.importFile(event)
.subscribe(
(importedExcelData: any) => (this.importedExcelData = importedExcelData)
);
}
}
组件模板保持不变
问题是,当尝试通过订阅函数使用 importFile 函数导入 excel 文件时,我在控制台中得到以下信息
在我的源代码中到处都是控制台日志,我可以确定的是服务函数在异步 fileReader.onload
有机会完成其工作之前返回 importedExcelData
- 因此它返回一个未定义的可观察对象,但研究了几个小时后,我似乎找不到可行的解决方案。作为最后的手段,我向 Whosebug 社区寻求帮助。如有任何帮助,我们将不胜感激。
如果我需要提供任何其他信息,请告诉我。
使用 Observable 来处理异步函数的想法看起来不错。唯一的问题是,目前还没有创建任何可观察对象。您可以使用 new Observable
构造创建一个。
尝试以下方法
import { Observable } from 'rxjs';
importFile(event: any): Observable<any> {
return new Observable(subscriber => {
const fileToUpload: DataTransfer = <DataTransfer>event.target;
if (fileToUpload.files.length !== 1)
subscriber.error('Can not upload multiple files'); // <-- emit error
const fileReader: FileReader = new FileReader();
fileReader.onload = (event: any) => {
const binstring: string = event.target.result;
const workbook: excelhandler.WorkBook = excelhandler.read(binstring, {
type: 'binary',
});
const worksheetName: string = workbook.SheetNames[0];
const worksheet: excelhandler.WorkSheet = workbook.Sheets[worksheetName];
const importedExcelData = excelhandler.utils.sheet_to_json(worksheet, {
header: 1,
});
subscriber.next(importedExcelData); // <-- emit notification
subscriber.complete(); // <-- complete and close the subscription
};
fileReader.readAsBinaryString(fileToUpload.files[0]);
});
}
由于我们要发送错误通知,您可以在订阅中传递一个 error
回调来捕获它们。
importFile(event: any) {
this.dataService.importFile(event).subscribe({
next: (importedExcelData: any) => this.importedExcelData = importedExcelData,
error: (error: any) => console.log(error)
});
}
可以找到有关 Observable 的更多信息here
我在 Angular 中构建了一个组件,它导入一个 excel 文件并将其转换为一个数组,然后将 excel 文件中的内容作为 table。如果我按如下方式在组件中构建函数,这将非常有效:
数据-import.compoent.ts
import { Component, OnInit } from '@angular/core';
import { DataImporterService } from 'src/app/services/data-importer.service';
import * as excelhandler from 'xlsx';
@Component({
selector: 'app-data-import',
templateUrl: './data-import.component.html',
styleUrls: ['./data-import.component.css'],
})
export class DataImportComponent {
// Properties
importedExcelData: any;
constructor() {}
importFile (event: any){
const fileToUpload: DataTransfer = <DataTransfer>event.target;
if (fileToUpload.files.length !== 1)
throw new Error('Can not upload multiple files');
const fileReader: FileReader = new FileReader();
fileReader.onload = (e: any) => {
const binstring: string = e.target.result;
const workbook: excelhandler.WorkBook = excelhandler.read(binstring, {
type: 'binary',
});
const worksheetName: string = workbook.SheetNames[0];
const worksheet: excelhandler.WorkSheet = workbook.Sheets[worksheetName];
this.importedExcelData = excelhandler.utils.sheet_to_json(worksheet, {
header: 1,
});
};
fileReader.readAsBinaryString(fileToUpload.files[0])
}
数据-import.component.html
<input type="file" (change)="importFile($event)" multiple="false" />
<table>
<tbody>
<tr *ngFor="let row of importedExcelData">
<td style="margin-left:5em" *ngFor="let cell of row">{{ cell }}</td>
</tr>
</tbody>
</table>
问题是我正在努力使我的组件尽可能“仅供演示” - 所以自然的步骤是将这个 importFile
函数移到returns 一个可观察对象在组件中订阅的服务如下:
数据-importer.service.ts
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import {} from 'rxjs/add/observable/fromPromise';
import * as excelhandler from 'xlsx';
@Injectable({
providedIn: 'root',
})
export class DataImporterService {
// Properties
importedExcelData: any
// Functions
importFile(event: any): Observable<any> {
const fileToUpload: DataTransfer = <DataTransfer>event.target;
if (fileToUpload.files.length !== 1)
throw new Error('Can not upload multiple files');
const fileReader: FileReader = new FileReader();
fileReader.onload = (event: any) => {
const binstring: string = event.target.result;
const workbook: excelhandler.WorkBook = excelhandler.read(binstring, {
type: 'binary',
});
const worksheetName: string = workbook.SheetNames[0];
const worksheet: excelhandler.WorkSheet = workbook.Sheets[worksheetName];
this.importedExcelData = excelhandler.utils.sheet_to_json(worksheet, {
header: 1,
});
fileReader.readAsBinaryString(fileToUpload.files[0]);
};
return this.importedExcelData
}
}
修正数据-import.component.ts
import { Component} from '@angular/core';
import { DataImporterService } from 'src/app/services/data-importer.service';
import * as excelhandler from 'xlsx';
@Component({
selector: 'app-data-import',
templateUrl: './data-import.component.html',
styleUrls: ['./data-import.component.css'],
})
export class DataImportComponent {
// Properties
importedExcelData: any;
constructor(private dataService: DataImporterService) {}
importFile(event: any) {
this.dataService
.importFile(event)
.subscribe(
(importedExcelData: any) => (this.importedExcelData = importedExcelData)
);
}
}
组件模板保持不变
问题是,当尝试通过订阅函数使用 importFile 函数导入 excel 文件时,我在控制台中得到以下信息
在我的源代码中到处都是控制台日志,我可以确定的是服务函数在异步 fileReader.onload
有机会完成其工作之前返回 importedExcelData
- 因此它返回一个未定义的可观察对象,但研究了几个小时后,我似乎找不到可行的解决方案。作为最后的手段,我向 Whosebug 社区寻求帮助。如有任何帮助,我们将不胜感激。
如果我需要提供任何其他信息,请告诉我。
使用 Observable 来处理异步函数的想法看起来不错。唯一的问题是,目前还没有创建任何可观察对象。您可以使用 new Observable
构造创建一个。
尝试以下方法
import { Observable } from 'rxjs';
importFile(event: any): Observable<any> {
return new Observable(subscriber => {
const fileToUpload: DataTransfer = <DataTransfer>event.target;
if (fileToUpload.files.length !== 1)
subscriber.error('Can not upload multiple files'); // <-- emit error
const fileReader: FileReader = new FileReader();
fileReader.onload = (event: any) => {
const binstring: string = event.target.result;
const workbook: excelhandler.WorkBook = excelhandler.read(binstring, {
type: 'binary',
});
const worksheetName: string = workbook.SheetNames[0];
const worksheet: excelhandler.WorkSheet = workbook.Sheets[worksheetName];
const importedExcelData = excelhandler.utils.sheet_to_json(worksheet, {
header: 1,
});
subscriber.next(importedExcelData); // <-- emit notification
subscriber.complete(); // <-- complete and close the subscription
};
fileReader.readAsBinaryString(fileToUpload.files[0]);
});
}
由于我们要发送错误通知,您可以在订阅中传递一个 error
回调来捕获它们。
importFile(event: any) {
this.dataService.importFile(event).subscribe({
next: (importedExcelData: any) => this.importedExcelData = importedExcelData,
error: (error: any) => console.log(error)
});
}
可以找到有关 Observable 的更多信息here