Use PDF Exporter to print content of table

I am trying to use the PDF_Exporter for a simple document. I have no problem getting data from specific components but can't seem to display the contents of a table or the data a query.
Im looking to display this on the PDF output as columns and rows. No special formatting needed just the data..

You should try option 2 defined here: Creating PDFs in Retool

Basically, you can use a table as usual and export the app visual as PDF

Thanks for the reply! I have utilized that post to get started.

The issue I run into with their "option 2" is that the PDF is just a replica of the container. If the table in the container has more lines than can be displayed in the container I don't think the 'screenshot' PDF will catch all the data.

I have also tried the 3rd option (Using an external API for PDF generation") and that works well, but I still don't know how to get all the table data there....

I could use something like is mentioned in this post: Not able to use PDF Exporter:
I have read every link on the google search "retool PDF". I have tried a few different things but I am just too new at this...

  1. Create a transformer that take your table3 source
  2. Convert the data to CSV: arrays - Converting JSON object to CSV format in JavaScript - Stack Overflow
  3. Use your transformer as source in the PDF Content instead of table3.data

For example, your transformer code may look like this

const source = {{ table3.data }} // You may replace this with the source used in table3 instead
function convertToCSV(arr) {
  return [Object.keys(arr[0])]
    .concat(arr)
    .map(it => {
      return Object.values(it).toString()
    }).join('\n')
}
return convertToCSV(source)

And your PDF Content may be changed to this

[...]
Table:
{{ transformer1.value }}
1 Like

There is also the built-in Papa Parse library if you strictly want CSV.

Depending on your source format

Papa.unparse(source)
// OR
Papa.unparse(formatDataAsArray(source))

The previous post allows more control on the format that you want

Just want to add an alternate here as well! At the moment, I'm not seeing a way to generate table borders with the PDF exporter but the following script will take table data and turn it into a markdown table which can be more effective for displaying small datasets:

function convertToMarkdown(data) {
const headers = [Object.keys(data[0])];
const breaks = [headers[0].map(() => "---")];
const rows = data.map((r) => Object.values(r));
const tableEntries = headers.concat(breaks).concat(rows);
return tableEntries.map((row) => "|" + row.join("|") + "|").join("\n");
}

1 Like

Thanks @Kabirdas, clean code & output!

An updated version of yours with right alignment for numbers :nerd_face:

const isNumber = (value) => typeof value === 'number' && isFinite(value)
const convertToMarkdown = (data) => {
  const headers = [Object.keys(data[0])];
  const breaks = [headers[0].map((fieldName) => isNumber(data[0][fieldName]) ? "--:" : "---")];
  const rows = data.map((r) => Object.values(r));
  const tableEntries = headers.concat(breaks).concat(rows);
  return tableEntries.map((row) => "|" + row.join("|") + "|").join("\n");
}

3 Likes

Good Job!

Hi @Kabirdas, I really appreciated your solution for exporting a table to a pdf. I managed to get your csv solution to work. Your solution to turn the data to a table hasn't worked for me because I am not sure what I need to change in my case. Does tableEntries refer to my table name, which is display1, such that tableEntries becomes display1 or display1Entries? Also, fieldName refers to one of the fields in the table but what about the rest? There are other entries such a value and data - do I need to make changes to these.
As you can see I am very new to this but working hard to understand!

For this to work, what did you put into PDF Content?

Hey @AlanT and @PatrickMast! The comment by @rferland above (this one) gives context for the solutions here. You can do this by creating a standalone transformer that looks something like:

const source = {{ table3.data }} // <--- this is where you'd pass your table data!

const isNumber = (value) => typeof value === 'number' && isFinite(value)
const convertToMarkdown = (data) => {
  const headers = [Object.keys(data[0])];
  const breaks = [headers[0].map((fieldName) => isNumber(data[0][fieldName]) ? "--:" : "---")];
  const rows = data.map((r) => Object.values(r));
  const tableEntries = headers.concat(breaks).concat(rows);
  return tableEntries.map((row) => "|" + row.join("|") + "|").join("\n");
}

return convertToMarkdown(source);

Then, your PDF content would be:

[...]
Table:
{{ transformer1.value }}

So in this case, tableEntries and fieldName are both variables that are being declared in the function itself so they can be left as is! The only thing that needs to change is {{ table3.data }} and you would just change that to {{ whateverYourTableNameIs.data }} :sweat_smile:

Let me know if that works!

Hi @Kabirdas, thank you for your reply - much appreciated. I have used your and @rferland solution in my app and it works just as it should and I understand where I was going wrong! Thank you both! It is a pity the lines don't appear in the PDF which would have been the 'icing on the cake'. The lines appear in the preview of the transformer which makes their absence in the PDF interesting!

Thank you for the solution. My conclusion is: PDF is hard todo in Retool. I'm using CraftmyPDF.com now which works awesome!