0

The requirement:

  1. The user selects multiple invoices from Angular table and clicks on download
  2. In a for loop the getClient method is executed to get client detail from server for each invoice
  3. Within the 'subscribe' of the getClient method, another method (serviceLogService.downloadInvoicePdf) is executed to download the invoice in PDF form.

Problem: When multiple invoices are selected, lets say 3 invoices, all the three invoices get downloaded - BUT all of them have the details of the last chosen invoice.

As I understand this is due to the async call of this.clientsService.getClient() getting executed one after another(in for loop) without waiting to execute the code within subscribe.

What would be the best way to handle this so that the required execution flow is completed before executing the next loop? Pls see at the bottom what I tried.

component.ts

  onDownload(){
    if(this.selection?.selected.length <= 0) return;
    const selectedInvoices = this.selection?.selected;

    for(const invoice of selectedInvoices) {
        this.clientsService.getClient(invoice.compId)
          .subscribe(client => {
            if(client) {
              this.serviceLogService.downloadInvoicePdf(this.cust, client, invoice);
            }
          });
    }
  }

clientsService

  getClient(id: string) {
    return this.httpClient.get<Client>(this.reqUrl + '/' + id);
  }

serviceLog-service

  downloadInvoicePdf(cust: Cust, client: Client, invoice: Invoice) {
    const invoiceName = client.compName + ' Inv-' + invoice.invNumber
    const isTaxInvoice = false;
    this.pdfService.generateDownloadInvPdf(cust, client, invoice, invoiceName, isTaxInvoice);
  }

pdfService

  async generateDownloadInvPdf(cust: Cust, client: Client, invoice: Invoice, fileName: string, isTaxInvoice: boolean) {
    await this.prepareInvoicePdf(cust, client, invoice, isTaxInvoice);
    await this.loadPdfMaker();
    this.pdfMake.createPdf(this.docDef).download(fileName);
  }

  async prepareInvoicePdf(cust: Cust, client: Client, invoice: Invoice, isTaxInvoice: boolean) {
    // ...code...

    this.docDef = { 
         // ... code
      }  
    }  


  async loadPdfMaker() {
    if(!this.pdfMake) {
      const pdfMakeModule = await import('pdfmake/build/pdfmake');
      const pdfFontsModule = await import('pdfmake/build/vfs_fonts');
      this.pdfMake = pdfMakeModule.default;
      this.pdfMake.vfs = pdfFontsModule.default.pdfMake.vfs;
    }
  }

Tried below async await but could not proceed due to the type error:

  async onDownload(){
    if(this.selection?.selected.length <= 0) return;
    const selectedInvoices = this.selection?.selected;

    for(const invoice of selectedInvoices) {
        const client = await this.clientsService.getClient(invoice.compId);
            if(client) {
              this.serviceLogService.downloadInvoicePdf(this.cust, client, invoice);
              // VSC error for arg client: 
              // Argument of type observable<Client> is not assignable to parameter of type 'Client'
            }
            
    }
  }
hemant
  • 377
  • 1
  • 2
  • 13
  • You mostlikely are sharing an object causing the data to change in the object and across your files. You should clone the data to be downloaded `const clone = JSON.parse(JSON.stringify(original))` – Get Off My Lawn Feb 27 '22 at 16:45

1 Answers1

0

As I see, you are trying to use 'await' on an Observable, that is not correct. 'await' can be used on promises, so if you are sure to keep using 'await', you can transform your Observable to a Promise like mentioned in the answer of 'How can I await on an Rx Observable?' For more information on the defference between Observable and Promise this post can help you.

KSoze
  • 141
  • 3