0

I have an angular 11 component that loops through elements of a page, and converts them from html > canvas > images. I am then appending them to a form.

My issue is that the promise always resolves after the 'done' console log runs. How can I await all generateCanvasFromHtml before continuing on with the script after the loops? Other than the running order, this function runs as expected

generateImagesFromPlan(): void {
    this.imageGenerating = true;

    // Create an empty for images to submit
    const formData = new FormData();

    // We get all the sections available on the plan
    const pdfSections = document.querySelectorAll('[data-pdfsection]');
    // tslint:disable-next-line:prefer-const
    for (let section of pdfSections as NodeListOf<HTMLElement>) {
      const sectionNumber = section.dataset.pdfsection;
      console.log('sectionName', sectionNumber)
      // We get all the partials available in the section (tables, headings, etc)
      const pdfComponents = section.querySelectorAll('[data-component]');
      console.log('pdfComponents', pdfComponents)
      // tslint:disable-next-line:prefer-const
      for (let element of pdfComponents as NodeListOf<HTMLElement>) {
        const componentImageNumber = element.dataset.component;
        console.log('componentName', componentImageNumber)
        // we create the image
        this.generateCanvasFromHtml(element).then(canvas => {
          canvas.toBlob((blob) => {
            formData.append(sectionNumber, blob, componentImageNumber + '.png');
            console.log('blob added');
          }, 'image/png');

        });
      }
    }

    for (let pair of formData.entries()) {
      console.log(pair[0] + ', ' + pair[1]);
    }
    console.log('done');
    this.imageGenerating = true;
  }

  generateCanvasFromHtml(elem: HTMLElement): Promise<HTMLCanvasElement> {
    // Put the content into an iframe with the width you want and you can obtain the actualy hegiht
    // Clone the element you want to capture
    const clone = elem.cloneNode(true);
    // Crate a dummy iframe
    const targetWidth = 1200;
    const iframe = document.createElement('iframe');
    iframe.style.visibility = 'hidden';
    iframe.width = targetWidth + 'px';
    iframe.height = '0px';
    document.body.appendChild(iframe);
    // Put cloned element into iframe
    const iframeDocument = iframe.contentDocument;
    iframeDocument.replaceChild(clone, iframeDocument.documentElement);
    // get scrollHeight of the iframe
    const targetHeight = iframeDocument.documentElement.scrollHeight;
    iframe.height = targetHeight + 'px';
    console.log('targetHeight=' + targetHeight);
    // Clean up
    document.body.removeChild(iframe);
    const options = {
      width: targetWidth,
      windowWidth: targetWidth,
      // Manually specify the height
      height: targetHeight
    };

    return html2canvas(elem, options);

  }

1 Answers1

2

There is Promise.all to await multiple promises.

So what you need to do is to create an array of generateCanvasFromHtml() executions from pdfComponents and feed this array to Promise.all.

alexanderdavide
  • 1,487
  • 3
  • 14
  • 22
  • Feeding this as an array into an Promise.all still seems to execute in an incorrect order. with Promise.all.then console log executing before the resolution of the blobs creation – Tristram Tolliday May 14 '21 at 11:24
  • `Promise.all` does not guarantee the order of execution. However, it resolves only when all given promises are resolved. If you need to preserve an order of execution, there is no way around a loop. See [this post](https://stackoverflow.com/a/24586168/9378384). – alexanderdavide May 14 '21 at 12:02