0

In my angular app i display JSON data in a table with pagination using ngx-pagination i want to export my table in pdf format I used a couple of npm packages that take a screenshot off the HTML page and convert it to a pdf to export, the only problem is not all the data is being displayed because of the pagination and number of items per page, is there a work around this?

Here is the code I used :

 <table
            class="
              table
              is-hoverable is-striped is-fullwidth
              content-table
              #userTable
            "
id="htmlData"
          >
            <thead>
              <tr>
                <th>{{ "DataSource.RecordID" | translate }}</th>
                <th colspan="6">{{ "DataSource.Details" | translate }}</th>
              </tr>
            </thead>
            <tbody>
              <tr
                *ngFor="
                  let row of FilteredMatchTransactions
                    | paginate: { itemsPerPage: itemsPerPage, currentPage: p };
                  let i = index
                "
              >
                <td>
                  {{ "DataSource.SessionID" | translate }} :
                  {{ row.SESSION_ID }}
                </td>
                <td>
                  {{ "DataSource.ExternalStan" | translate }} :
                  {{ row.EXTERNAL_STAN }}
                </td>
                <td>
                  PAN :
                  {{ row.CARD_NUMBER }}
                </td>
                <td>
                  {{ "DataSource.Amount" | translate }} :
                  {{ row.TRANSACTION_AMOUNT.split(" ")[0] }}
                </td>
                <td>
                  {{ "DataSource.Currency" | translate }} :
                  {{ row.TRANSACTION_AMOUNT.split(" ")[1] }}
                </td>
                <td>
                  Terminal :
                  {{ row.TERMINAL_ID }}
                </td>
                <td>
                  {{ "DataSource.TransactionDate" | translate }} :
                  {{ row.TRANSACTION_DATE }}
                </td>
              </tr>
            </tbody>
          </table>

ts :

import jsPDF from 'jspdf';
import html2canvas from 'html2canvas';
....
....
public downloadPDF(): void {
    let DATA = document.getElementById('htmlData');
    console.log(DATA);
    html2canvas(DATA).then((canvas) => {
      let fileWidth = 208;
      let fileHeight = (canvas.height * fileWidth) / canvas.width;

      const FILEURI = canvas.toDataURL('image/png');
      let PDF = new jsPDF('p', 'mm', 'a4');
      let position = 0;
      PDF.addImage(FILEURI, 'PNG', 0, position, fileWidth, fileHeight);

      PDF.save('angular-demo.pdf');
    });
  }
MARK
  • 75
  • 1
  • 11
  • Hello @SOUHAIL, " the only problem is not all the data is being displayed because of the pagination and number of items per page..", i want to know which data is displayed and which data is not ? if you meant by that "you are only rendering data in `the first page`, so that would be normal because all data are saved in the DB and will be charged only by Page Event changes.. in that case it would be better to generate your PDF in the backend part of your application instead of the front part. – sohaieb azaiez May 29 '21 at 13:29
  • hey @sohaiebazaiez yes only the first page is being displayed but the `FilteredMatchTransactions` contains all the JSON data from the api and the pagination is only splitting it up into the number of items per page – MARK May 29 '21 at 13:49
  • yes you are right, but as i saw, you are taking a snapshot only from the **currently displayed data html**, so that means a snapshot of the **first page** so that others pages will never be included if you don't paginate into it manually.. Angular works often with **ChangeDetector** process to refresh the view by binded data, so it will not consider all other data. As a solution, its better to **unify** all your data in **one page html table or element**, or to create a **hidden table DOM** with document.createElement('table') and set your data there then consume it for pdf creation. – sohaieb azaiez May 30 '21 at 11:22
  • Hey, @sohaiebazaiez thank you for your response do you mind elaborating on how it can exactly be done? – MARK May 30 '21 at 17:30
  • hello @SOUHAIL, yes i have no problem – sohaieb azaiez May 31 '21 at 08:21
  • @sohaiebazaiez can you please put it as an answer ? – MARK May 31 '21 at 08:59
  • Hello bro, i'm sorry for my late answer because i don't connect too much.. but i will try to write you my answer in an **overview** to let you know about the global idea and i will let you implement the rest of the code. – sohaieb azaiez Jun 01 '21 at 09:08
  • @sohaiebazaiez that would be helpful thank you – MARK Jun 01 '21 at 09:18
  • you are welcome, check my answer below, i hope it can help at least in some points – sohaieb azaiez Jun 01 '21 at 09:53

1 Answers1

1

my idea is about to use a temporary created table DOM element instead of using angular active table (situated in the view), this is an overview of the idea:
// I suppose that this jsonApiData is an array got from API which you used it in DataSource and containing all data without pagination, so you should be sure that you already loaded all data from the API before performing any data manipulation or consumption:
private jsonApiData;

  
public downloadPDF(): void {
    /*let table = document.getElementById('htmlData');*/

    // refer to getTemporaryTableDom method below
    let table = this.getTemporaryTableDom(this.jsonApiData);

    html2canvas(table).then((canvas) => {
      ...
      ...
      // Important: after finishing your save, remove the temporary table DOM from the memory
      table.remove(); ==> to free up your memory.
    });
  }

...
...
/** this method will create a temporary table dom,
  * and fill it with your api data.
 **/
private getTemporaryTableDom(jsonData){

  let t = document.createElement('table');

// fill your DOM Html table with your api data 
  for(let [index,obj] of Object.entries(jsonData)){   
    let newInsertedRow = t.insertRow(index); 

    // to insert cells and its values you can also use loop, i will let you free to chose
    c = newInsertedRow.insertCell(0); // example SessionID 
    c.innerHTML = obj.SessionId; // SessionID value

    c = r.insertCell(1); // example: ExternalStan
    c.innerHTML = obj.ExternalStan; // ExternalStan value
    ...
    ...
  }

 return t; // to return the DOM TAble html element

}

This is just an idea to make sure you are using static data so that you will be able to control it without any problem, because using the angular view Doms can make things more difficult to manage because they are based on many concepts like ChangeDetection, component lifecycle, etc..

for more table dom manipulation details, you can refer to these ressources:

sohaieb azaiez
  • 768
  • 9
  • 20