Angular在执行生成Excel之前等待订阅。

huangapple go评论59阅读模式
英文:

Angular wait for subscription before executing function to generate an excel

问题

我有一个函数,从API请求生成Excel。在网页上,我有一个下载按钮,执行函数downloadCSV()。它目前按预期工作,除非我在导航到页面时点击链接太快。

在函数中,我订阅数据并使用它填充一个数组,然后用它生成Excel。无论数组是否被填充,Excel都会立即下载。

我希望要么在Excel为空时重试该函数。

要么等到订阅完成后再执行。

downloadCSV(): void {
  // 列出标题
  const csvHeaders = [
    '一堆标题'
  ];

  // 初始化数组
  const data: any[] = [];
  // 获取每个客户的姓名
  this.nameService.getNames().subscribe({
    next: ({ names }) => {
      // 对于每个客户,列出详细信息
      names.forEach(name => {
        // 获取详细信息
        this.nameService.getPersonalData(name.firstName).pipe(
          tap((data: Idata) => {
            data.push({
              '一堆数据点'
            });
          })
        ).subscribe(
          { error: (err) =>
          { console.error(err); } });
      });
    }
  });
  const filename = '数据报告';
  this.generateCSV(data, filename, csvHeaders);
}

generateCSV(data, filename, reportHeader): void {
  new ngxCsv(data, filename, { headers: reportHeader, showTitle: true, title: this.customerDetails });
}

所以对于这个函数,我需要获取姓名,然后使用名字获取我想要的实际数据。然后订阅并生成Excel。

目前,Excel会立即下载,而不等待数据填充。

英文:

I have a function where I'm generating an excel from an api request. on the webpage I have a download button that executes the function downloadCSV(). It currently works as expected unless I click on the link too fast when navigating to the page.

In the function I'm subscribing to the data and using it to fill an array which is then used to generate an excel. The excel will download instantly regardless of whether the array is populated.

I'm looking to either retry the function if the excel is empty.

OR

Wait until the subscription is finished to execute.

downloadCSV (): void {
//list headers
const csvHeaders = [
 'bunch of headers'
];

//initate array
const data: any[] = [];
//get name for each customer
this.nameService.getNames().subscribe({
  next: ({ names }) => {
    //for each customer list details 
    names.forEach(name => {
      //get details
      this.nameService.getPersonalData(name.firstName).pipe(
        tap((data: Idata) => {
          data.push({
            'bunch of data points'
          });
        })
      ).subscribe(
        { error: (err) =>
        { console.error(err); } });
    });
  }
});
const filename = 'dataReport';
    this.generateCSV(data, filename, reportHeaders);

}

  generateCSV (data, filename, reportHeader): void {
    new ngxCsv(data, filename, { headers: reportHeader, showTitle: true, title: this.customerDetails });
  }

So for the function I need to getNames, then use the firstname to get the actual data I want. Then subscribe and generate an excel.

For now the excel is instantly downloading without first waiting to be populated by data.

答案1

得分: 1

最好的方法是等待所有订阅结束。

如果你的 Excel 表格为空,可能有完全不同的原因。你可能希望针对这些情况进行特殊处理。
如果你的请求很大,谁知道你将不得不重试多少次?
在大多数情况下,随机重试不是一个好方法。

所以在你的情况下,我认为 rxjs 中的 forkJoin 操作符将起到作用。

简单来说,它将把所有的可观察对象合并成一个。这样,最终只会有一个订阅(用于所有的 getPersonalData 请求)。
forkJoin 只有在所有可观察对象都发出一次值时才会发出一个值,这正是你想要的。

所以你的代码将类似于这样:

this.nameService.getNames().subscribe({
      next: ({ names }) => {
        // 对于每个客户列出详细信息
        forkJoin(
          names.map((name) => {
            // 获取详细信息
            return this.nameService.getPersonalData(name.firstName)
              .pipe(
                 catchError((err) => {
                    console.error(err);
                 }));
          }),
        ).subscribe((datas: Idata[]) => {
          // 在使用数据之前对数据执行任何操作
          this.generateCSV(datas, filename, reportHeaders);
        });
      },
    });

你等待所有请求发出,然后调用你的方法。

PS:我没有运行代码,所以可能会有一些语法错误。

PS2:不要忘记从所有的可观察对象中取消订阅!

英文:

The best way is to wait for all the subscriptions to end.

If your excel is empty it can be for completely different reasons. And you probably want to handle them specifically.
And if your requests are big who knows how many time you will have to retry ?
In most of cases randomly retrying is not the good way.

So in your case I think the forkJoin operator from rxjs will do the job.

To put in simply it will fuse all your observable into one. So you will have only one subscription in the end (one for all the getPersonalData requests).
The forkJoin will emit a value only when ALL the observables have emitted once, which is what you want.

So your code will look like something like this

this.nameService.getNames().subscribe({
      next: ({ names }) => {
        //for each customer list details
        forkJoin(
          names.map((name) => {
            //get details
            return this.nameService.getPersonalData(name.firstName)
              .pipe(
                 catchError((err) => {
                    console.error(err);
                 }));
          }),
        ).subscribe((datas: Idata[]) => {
          // Do whatever you want with datas before using it
          this.generateCSV(datas, filename, reportHeaders);
        });
      },
    });

You wait for all the requests to emit and then you call your method.

PS: I didn't run the code so they might be some syntax mistake

PS2: Don't forget to unsubscribe from all your observables !

huangapple
  • 本文由 发表于 2023年3月8日 18:45:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/75672013.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定