What is the best way to dynamically insert content into a PDF using pdfmake?

In my quest to dynamically generate PDFs using pdfmake, I've encountered an issue with creating dynamic rows based on data.

To illustrate, here is a simplified version of the code:

getDocumentDefinition(img: string, data: DataResponse, user: UserResponse): TDocumentDefinitions {
    return {
        header: this.header(img),
        content: this.document(data, user),
        styles: this.applyStyles(),
    };
}

private document(data: DataResponse, user: UserResponse) {
     let rowY = 252;
     return [
         // works
         this.createRow(55, rowY, 1, { m: 10, s: 30 }, [163, 205], rowY + 6)

         // not working in loop
         data.map((item) => this.createRow(55, rowY, item.version, { m: item.minutes, s: item.seconds }, [163, 205], rowY + 6)).map(() => rowY += 34),
     ];
}

private createRow(rowX: number, rowY: number, version: number, time: { m: number; s: number }, x: number[], y: number): Content {
     return [
         this.createTable(10, version, time, {x: rowX, y: rowY}),
         this.circle(),
         this.circle(),
     ];
}

private createTable(heights: number, version: number, time: { m: number, s: number }, position: { x: number; y: number }): Content {
     return {
         table: {
             widths: ['10%', '30%', '30%', '30%'],
                heights: heights,
                body: [
                  [
                     {
                         text: version,
                         fontSize: 10,
                         borderColor: ['white', 'white', 'white', '#B3CCE6'],
                      },
                      {
                         text: time.m + 'm ' + time.s + 's',
                         fontSize: 10,
                         borderColor: ['white', 'white', 'white', '#B3CCE6'],
                      },
                      {
                         text: '',
                         fontSize: 10,
                         borderColor: ['white', 'white', 'white', '#B3CCE6'],
                      },
                      {
                         text: '',
                         fontSize: 10,
                         borderColor: ['white', 'white', 'white', '#B3CCE6'],
                      },
                   ],
                ],
            },
            absolutePosition: position,
        };
}

The method createRow functions as expected when used singularly, but when attempted to be generated dynamically (as shown in the example), the result is empty. I have tried various loops and methods but have been unable to resolve the issue. Is there a way to add content based on an array with an unknown size?

Answer №1

  1. You might want to double check your return array in the document method for missing commas, particularly after this.createRow
  2. Remember that data.map() returns an array, so nesting it within another array will result in a nested array structure
  3. Make sure the Y value is dynamic for each row; it should not be the same value for all rows

Consider implementing something similar to this:

private document(data: DataResponse, user: UserResponse) {
    let rowY = 252;

    const staticContent = [
        this.createRow(55, rowY, 1, { m: 10, s: 30 }, [163, 205], rowY + 6)
    ];

    const dynamicContent = data.map((item, index) => {
        let updatedRowY = rowY + index * someYIncrementValue; // Adjust as needed
 
        return this.createRow(55, updatedRowY, item.version, { m: item.minutes, s: item.seconds }, [163, 205], updatedRowY + 6)
    });

    return [...staticContent, ...dynamicContent];
}

Answer №2

Expand your result array of map operation. For example:

private document(data: DataResponse, user: UserResponse) {
     let rowY = 252;
     return [
         ...data.map((item) => this.createRow(55, rowY, item.version, { m: item.minutes, s: item.seconds }, [163, 205], rowY + 6)).map(() => rowY += 34),
     ];
}

Alternatively, you can use this approach:

private document(data: DataResponse, user: UserResponse) {
         let rowY = 252;
         return data.map((item) => this.createRow(55, rowY, item.version, { m: item.minutes, s: item.seconds }, [163, 205], rowY + 6)).map(() => rowY += 34);
    }

Explanation: When the function createRow is called without a loop, it returns as:

[  [
         this.createTable(10, version, time, {x: rowX, y: rowY}),
         this.circle(),
         this.circle(),
     ] ]

But when it is used in a loop, it adds an extra array due to the nature of the map loop returning an array itself:

[ 
 [
  [
    this.createTable(10, version, time, {x: rowX, y: rowY}),             
    this.circle(),
    this.circle(),
  ] 
 ]
] 

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Implementing the same concept yet yielding diverse variations?

I'm a bit confused as to why a1 is evaluated as false while a2 is considered to be of type boolean. Can someone explain the reasoning behind this discrepancy? type Includes<T extends readonly any[], U> = U extends T[number] ? true : false; type ...

Utilizing TypeScript to enhance method proxying

I'm currently in the process of converting my JavaScript project to TypeScript, but I've hit a roadblock with an unresolved TypeScript error (TS2339). Within my code base, I have a class defined like this: export const devtoolsBackgroundScriptCl ...

Angular design featuring numerical staircase pattern

I'm attempting to implement a numeric version of the staircase pattern using Angular, but I have yet to find the solution. Here is the desired outcome: Below is the code I have developed thus far: main.ts import { Component, Input, OnInit } from &ap ...

Set the styling of a div element in the Angular template when the application is first initialized

I have a question about this specific div element: <div class="relative rounded overflow-clip border w-full" [style.aspect-ratio]="media.value.ratio"> <img fill priority [ngSrc]="media.value.src || ftaLogo" ...

Incompatibility with semantic versioning and npm versions 5 and higher

Could you explain the necessity of using NPM 5 or later to prevent issues with semantic versioning? How does the package-lock.json file help in avoiding this problem? Would using the same package.json file on all development machines for a project also r ...

Is there a way to close a window in JavaScript that was opened using Ionic Capacitor?

Currently, I am trying to open a window within my Ionic app by using the code snippet below: await Browser.open({url: environment.apiUrl + `/myurl`}); However, upon completion of a certain action by the user, I want to close that same window. Unfortunate ...

Using the @Input directive in Angular to bind the disable attribute to a button component

I'm currently in the process of developing a button directive that has the ability to receive a boolean value through the @Input and subsequently bind it to the disabled attribute of the <button> element. Here is a snippet of what I have been w ...

Automatically update data in Angular without the need to refresh the page

One feature of my application involves displaying a table with rows retrieved from a database. The functionality responsible for fetching this data is an AJAX call, implemented as follows: getPosts(): Observable<Posts[]> { return this.http.post ...

Is the Angular maxlength parameter causing issues with user input?

Previously, the old ng-maxlength="5" would trigger a field error but allow user input to continue. Now, with maxlength="5", it seems that input is being prevented altogether. I have novalidate on my form - could Angular be causing this? Should input be all ...

How can I compel npm to resolve dependencies flatly?

I am working on a project where multiple frontends share a common library. The module dependencies for these projects are managed using npm. In the package.json file of each project, I specify: "dependencies": { "mylib": "file:../<...path...> ...

Passing HTML content to an ng-bootstrap modal in Angular 2+

My modal setup in the Component Library looks like this. Keep in mind, I am working within a Component Library, not just the application. Within my Component Library... The template is as follows: <div class="modal-header"> <h4 class="mt- ...

Repeatedly view the identical file on HTML

I'm currently working with the following HTML code: <input type="file" style="display: none" #file(change)="processImage($event)" /> <button type="button" class="btn" (click)="file.click()" Browse </button> When I select image1 fr ...

Incorporate any enum value into a Typescript interface

I'm working with a Typescript interface export interface MyInterface { valid: boolean; resourceType: MyEnum; message: string; } As well as an enum enum MyEnum { 'a', 'b', 'c' } Is there a way to allow the ...

Analyzing a sizable JSON file serving as the data source for a PostgreSQL database

Currently, I am working on a Next.js project that involves a large JSON file (~65,000 lines) serving as data for a Prisma Postgres database. The structure of the file includes entries like the following: [ { "NativeClass": "class-name", "Classes" ...

Having trouble accessing the object from @Input

My issue revolves around system.component.html <div *ngFor="let tab of tabs | async"> <app-tab [tab]="tab"></app-tab> </div> In tab.component.ts, I wrote the following code: export class TabComponent implements OnInit { @Inpu ...

Issues with Angular unit tests failing due to an unexpected phantomJS error

Executing ng test triggers the execution of my 3 unit tests which have been hardcoded to pass successfully, as shown below: describe('AppComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ ...

Unlock the encrypted information in the blockchain

I've been working on encrypting and decrypting values using Node's built-in crypto module. I found a helpful tutorial that showed me how to encrypt the data, but it didn't provide any sample code for decryption. When I tried using code from ...

Unable to perform module augmentation in TypeScript

Following the guidelines provided, I successfully added proper typings to my react-i18next setup. The instructions can be found at: However, upon creating the react-i18next.d.ts file, I encountered errors concerning unexported members within the react-i18 ...

What is the best way to define a precise return type for a JSX Element?

Is it possible to define a function that returns a Button element and what would the correct return type of the function be? For example: Ex: const clickMeButton = (): Button => { return ( <Button> Click Me </Button& ...

Error message: When working with Protobuf, an uncaught reference error occurs because the 'exports

Currently, I am in the process of configuring protobuf to work with typescript. According to the official Google Documentation, all that is needed is to execute npm install google-protobuf and then to include require('google-protobuf'). Unfortu ...