【问题标题】:Stuggling to use function with async onload method with in a service in Angular - same function works perfect in component?在Angular的服务中使用异步onload方法的功能 - 相同的功能在组件中完美运行?
【发布时间】:2021-09-08 02:47:17
【问题描述】:

我在 Angular 中构建了一个组件,它导入一个 excel 文件并将其转换为一个数组,然后将页面上的 excel 文件上的内容显示为一个表格。如果我按如下方式在组件中构建功能,这将非常有效:

data-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])
  }

data-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 函数移动到返回可观察订阅的服务中在组件中如下:

data-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

  }
}

修订后的 data-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 - 因此它返回一个未定义的可观察对象,但是经过几个小时的研究,我似乎找不到可行的解决方案。作为最后的手段,我向 StackOverFlow 社区寻求帮助。任何帮助将不胜感激。

如果我需要提供任何其他信息,请告诉我。

【问题讨论】:

    标签: javascript angular typescript service components


    【解决方案1】:

    使用 Observable 处理异步函数的想法看起来不错。唯一的问题是,目前没有创建 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)
      });
    }
    

    更多关于 Observables 的信息可以在here找到

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-01-11
      • 1970-01-01
      • 2020-02-04
      • 2017-08-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多