pdfmake.min.js a dynamiczne tworzenie treści do generowania PDF

0

Prośba o pomoc.

Pdfmake wykorzystuje obiekty JSON w celu konwertowania zawartości html do pliku PDF, jako nośniki treści, które mają się pojawić na pdf.
Przykładowa zmienna:

     var dd = {
       content: [{
         table: {


           body: [
             ['Kolumna1', 'Kolumna2', 'Kolumna3', 'Kolumna4'],
             ['Wiersz' + i1 , b, c, d],
             ['Wiersz' + i2 , b2, c2, d2],
           ]
         }
       }]
     }

Daje w efekcie taką tabelkę o 4 kolumnach i trzech wierszach.
Czy jest jakiś prosty sposób aby móc dynamicznie tworzyć zmienną dd w taki sposób aby móc dodawać kolejne wiersze do body: czyli kolejne wiersze tabelki oraz kolejne table: do content: czyli dodawać kolejne całe tabelki np. w postaci stringów zapisanych w innych zmiennych lub w postaci tablic?

1

Oczywiście. Budujesz sobie stronę, która dodaje do tablicy kolejne wiersze, a gdy jesteś gotowy - uruchamia funkcję generującą PDF.

Bardzo prymitywny przykład daję poniżej. Dużo lepiej byłoby uzupełniać tylko zmienną przedmioty, a generowaniem <table> powinien się zająć jakiś framework - ale mam wrażenie, że to jeszcze nie Twój poziom.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>JS Bin</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.4/pdfmake.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.4/vfs_fonts.js"></script>
</head>
<body>
<table>
    <tr>
        <th>Przedmiot</th>
        <th>Cena</th>
    </tr>
</table>

<form>
    przedmiot: <input name="przedmiot" /><br />
    cena: <input name="cena" /><br />
    <button>dodaj</button>
</form>

<button id="gen">Pobierz pdf</button>

<script>
    const przedmioty = [];

    const form = document.querySelector("form");
    form.addEventListener("submit", (e) => {
        e.preventDefault();

        const przedmiot = e.currentTarget.elements.przedmiot.value;
        const cena = e.currentTarget.elements.cena.value;

        przedmioty.push([przedmiot, cena]);

        const tr = document.createElement("tr");
        const td1 = document.createElement("td");
        const td2 = document.createElement("td");

        td1.textContent = przedmiot;
        td2.textContent = cena;
        tr.appendChild(td1);
        tr.appendChild(td2);

        document.querySelector("table").appendChild(tr);
    });

    const gen = document.getElementById("gen");
    gen.addEventListener("click", () => {
        const pdf = createPdf({
            content: [
                {
                    table: {
                        widths: ["*", "*"],
                        body: przedmioty
                    }
                }

            ]
        });
        pdf.download();
    })


</script>
</body>
</html>
0

@dzek69: Dzięki za pomoc. Wiem zatem jak generować dowolną liczbę wierszy w zależności od dostępnych zmiennych. Ale jak wstawiać w jakiś prosty sposób dowolną ilość tabelek (lub innych treści dynamicznie) i znów określać ilość ich wierszy. Próbowałem metodą dd= JSON.parse(text); gdzie dd to zmienna do wydruku, a text to odpowiednio przygotowany wcześniej string już uwzględniający docelową ilość tabel. Ale zupełnie nie rozumiem czemu to nie działa w przypadku pdf make.
Czy dobrze myślę, że rozwiązaniem jest tutaj tworzenie zmiennej za pomocą builderFactory.createObjectBuilder()

0

Nie mam pojęcia o czym mówisz, co jest w tym stringu, który parsujesz przez JSON.parse, ani skąd Ci się wzięło builderFactory.createObjectBuilder() co mi brzmi jak jakaś Java, a nie JavaScript (to dwa odmienne języki!)

0

@dzek69: Co do JSON.parse to myślałem, że mogę zmienną pdf z przykładu, który podałeś, pozyskiwać ze zwyczajnego stringa za pomocą właśnie JSON.parse() i wtedy createPdf() w nawiasie wstawiać tą właśnie przeprasowaną zmienną. Jak zrozumiałem służy to do przerabiania stringów na takie właśnie obiekty.
Co do drugiej części to faktycznie zonk. To java.

0

Jak najbardziej możesz to robić, tylko jeżeli to nie działa to zdaje się, że Twój string nie odpowiada temu, co powinieneś dostać w rezultacie.
Możesz wkleić tu ten string?

0

@dzek69:


<!DOCTYPE HTML>
<html lang="pl">
<head>
  <meta charset="utf-8" />
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.4/pdfmake.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.4/vfs_fonts.js"></script>
  <script type="text/javascript">

function pobierz() {

  a1 = 1
  a2 = 2
  a3 = 3
  a4 = 4

  const przedmioty = [];
  for (i = 1; i <= 4; i++) {

    var n = "a" + i
    przedmioty.push(["wiersz" + i, window[n]]);
  }
  const text = '{content:[{table:{widths:["*", "*"],body: przedmioty }}]}'
  const przedmioty1 = JSON.parse(text);

  const pdf = createPdf(przedmioty1);
  pdf.download();
}
  </script>
  
</head>


<body>

  <input type="button" value="Zapisz PDF" onclick="pobierz()" />
 
</body>

A działa gdy zmienną PDF zapisuje jak w podanym przez Ciebie przykładzie.

0

String nie może odwoływać się do zmiennej przedmioty, parsowanie JSON tak nie działa. Po co Ci w ogóle string i to parsowanie w tym miejscu? Nie widzę w tym sensu.

Opisz swój projekt szerzej, spróbujemy dobrać rozwiązanie do problemu zamiast łatać złe rozwiązanie byleby działało.

1

Ogólnie JSON jest niepoprawny już w drugiej literce (nazwy właściwości obiektu powinny być w cudzysłowach), co powinieneś zauważyć (i zaraportować tutaj) w konsoli narzędzi deweloperskich przeglądarki - dobry moment, żeby nauczyć się z niej korzystać

0

OK może wyjaśnię o co mi chodzi trochę w przykładzie, a trochę opisując.
Mam 4 zmienne - a1b1, a1b2, a1b3, a1b4, które reprezentują 4 kluczowe informacje do prezentacji w wierszach w tabeli. Cyfra po "b" w nazwie odpowiada wierszowi tabeli.
Pytanie sprowadza się do tego co zrobić żeby generować pdfa z powtarzalnymi osobnymi tabelkami w tym samym pdfie gdyby takich czwórek powtarzalnych zmiennych było wielokrotność czyli np dalej a2b1, a2b2, a2b3, a2b4 potem a3b1, a3b2, a3b3, a3b4 itd., przy czym to ile wystąpi czwórek nie jest oczywiście z góry ustalone zależy od użytkownika - mooże być 1 a może być 115 . Zdaje sobie sprawę, że można zamiast mojego dziwnego nazewnictwa zmiennych stosować tablicę dwuwymiarową, ale w temacie, o który pytam chyba nie robi to różnicy.
Dla tego wydało mi się wygodne korzystanie ze zwykłego stringa, bo wtedy łatwo przy użyciu konkatenacji zbudować to co potrzebne i myślałem, że da się to "przerobić" na zmienną o konstrukcji, z której korzysta pdfmake, ale o zadanej liczbie tabelek.


<!DOCTYPE HTML>
<html lang="pl">
<head>
  <meta charset="utf-8" />
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.4/pdfmake.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.4/vfs_fonts.js"></script>
  <script type="text/javascript">

function pobierz() {

  a1b1 = 100
  a1b2 = 200
  a1b3 = 300
  a1b4 = 400


  const przedmioty = [];
  for (i = 0; i <= 4; i++) {
    var n = "a1b" + i
    if (i == 0) przedmioty.push(["Nazwa kolumny 1", "Nazwa kolumny 2"]);
    if (i > 0) przedmioty.push(["Opis wiersza" + i, window[n]]);
  }
  const pdf = createPdf({
    content: [{
      table: {
        widths: [70, 70],
        body: przedmioty,
      }
    }, ]
  });
  pdf.download();
}
  </script>

</head>

<body>

  <input type="button" value="Zapisz PDF" onclick="pobierz()" />

</body>

Inaczej mawiać żeby osiągnąć efekt drukowania czegoś takiego, przy czym nie konkretnej ilości tabelek (tutaj 2) tylko ilości w razie potrzeb, w zależności od dostarczonych danych.

const pdf = createPdf({
    content: [{
      table: {
        widths: [70, 70],
        body: przedmioty,
      }
    }, 
{
      table: {
        widths: [70, 70],
        body: przedmioty1,
      }
    },]
  });
  pdf.download();
}
0

Zdaje sobie sprawę, że można zamiast mojego dziwnego nazewnictwa zmiennych stosować tablicę dwuwymiarową, ale w temacie, o który pytam chyba nie robi to różnicy.

Trochę robi - nie dość, że to brzydka praktyka, to dodatkowo - niepraktyczna - wydłużasz dalszą część kodu. Jeżeli ilość potrzebnych zmiennych jest dynamiczna - to znaczy, że powinieneś coś wsadzić w tablicę/obiekt. Dodatkowo zmienne globalne (trzymanie danych w window) to też brzydka praktyka, która potrafi się zemścić z najmniej spodziewanym momencie. Nie rób tego.

Dla tego wydało mi się wygodne korzystanie ze zwykłego stringa, bo wtedy łatwo przy użyciu konkatenacji zbudować to co potrzebne i myślałem, że da się to "przerobić" na zmienną o konstrukcji, z której korzysta pdfmake, ale o zadanej liczbie tabelek.

Kolejna brzydka praktyka, często prowadząca do po prostu błędów albo bardziej zaawansowanej formy błędów: dziur bezpieczeństwa. Przerabianie wszystkiego na stringi i z powrotem nie ma najmniejszego sensu. Nie ucz się tego, bo teraz wyjdzie Ci brzydka tabelka albo przycisk nie za działa, a za 5 lat będziesz odpowiedzialny za jakiś głośny wyciek danych ;)

Pytanko jeszcze, skąd w ogóle biorą się te dane?

  a1b1 = 100
  a1b2 = 200
  a1b3 = 300
  a1b4 = 400

Na sztywno w kodzie ich chyba nie chcesz mieć?

0

@dzek69: Biorą się z formularza, który jest dynamiczny to znaczy zawiera nieokreślona liczbę powtarzalnych tabelek zawierających inputy. W tym przykładzie tabelka ma 4 pola do wypełnienia. Tabelek może byc1 a może być 500. Zmienne tworzę w sposób, który domyślam się, że jest brzydki czyli document.getElement.ById za pomocą dwóch pętli. Pierwsza wykonywana jest tyle razy ile jest tabelek. Dzięki temu mam liczbę po "a" w nazwie zmiennej. Druga pętla w środku wykonywana jest tyle razy ile tabelka ma pól czyli 4 w tym przypadku. Dzięki temu mam liczbę po "b" w nazwie zmiennej i oczywiście wartość zmiennej. Formularz służy przygotowaniu danych w formacie xml do pobrania na zewnątrz (z tym sobie radzę), ale też ma dać możliwość wizualizacji tego co wypełniono w formie PDFa, czyli po to aby w zbliżony sposób do tego w jaki się go wypełnia dać wydruk.

I po wielkich bólach jestem w domu:

<!DOCTYPE HTML>
<html lang="pl">
<head>
  <meta charset="utf-8" />
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.4/pdfmake.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.4/vfs_fonts.js"></script>
  <script type="text/javascript">

function pobierz() {

  var doc = {
    content: ['Nazwa tabelki\n\n']
  };

  a1b1 = 110
  a1b2 = 120
  a1b3 = 130
  a1b4 = 140
 
  a2b1 = 210
  a2b2 = 220
  a2b3 = 230
  a2b4 = 240
 
  a3b1 = 310
  a3b2 = 320
  a3b3 = 330
  a3b4 = 340

  for (k = 1; k <= 3; k++) {
    const przedmioty = [];
    for (i = 0; i <= 4; i++) {
      var n = "a" + k + "b" + i
      if (i == 0) przedmioty.push(["L.P." + k, "Nazwa kolumny"]);
      if (i > 0) przedmioty.push(["Opis wiersza" + i, window[n]]);
    }
    doc.content.push({
        table: {
          widths: [90, 50],
          body: przedmioty
        }
      },
      '\n'
    );
  }
createPdf(doc).download("nazwa_pliku");
}
  </script>
</head>
<body>
  <input type="button" value="Zapisz PDF" onclick="pobierz()" />
</body>
1

I po wielkich bólach jestem w domu:

W sensie zadowala Cię to rozwiązanie i nie potrzebujesz dalej pomocy?

0

@dzek69: Ok powiedzmy, że zrobię to bardziej po bożemu i dane trzymam w tablicy. Czy jest jakiś lepszy sposób tworzenia tych tabelek niż zaprezentowany poniżej. Chodzi mi oczywiście nie o tworzenie danych w tablicy tylko ich wykorzystanie w tabelkach powtarzalnych.
W szczególności jak dodawać za pomocą push elementy bezpośrednio do tablicy "body" bez przechodzenia przez "przedmioty".

<!DOCTYPE HTML>
<html lang="pl">
<head>
  <meta charset="utf-8" />
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.4/pdfmake.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.4/vfs_fonts.js"></script>
  <script type="text/javascript">

function pobierz() {

  roboczezrodlodanych = 100
  glownezrodlodanych = []

  for (k = 0; k < 3; k++) {
    glownezrodlodanych[k] = []
    for (i = 0; i < 4; i++) {
      roboczezrodlodanych += 10;
      glownezrodlodanych[k][i] = roboczezrodlodanych;
    }
  }

//powyższa część mało istotna służy jedynie roboczemu wytworzeniu danych na potrzeby przykładu, 
//w rzeczywistości główne źródło danych pozyskane w inny sposób, ale też jako tabela o entej liczbie
// wierszy na 4 kolumny (w przykładzie 3 wiersze 4 kolumny)

  var doc = {
    content: ['Nazwa tabelki\n\n']
  };
  for (k = 0; k < 3; k++) {
    const przedmioty = [];     //rozumiem, że słuszniej byłoby w tym miejscu pchać tabelkę z pustą tablicą
 //body zamiast tworzyć "przedmioty" i pośrednio przez nie przechodzić
    for (i = 0; i < 4; i++) { //a w tym miejscu już tylko pchać do body kolejne elementy tablicy -
//["Opis wiersza" + (i + 1), glownezrodlodanych[k][i]]. I tego nie wiem jak zrobić

      przedmioty.push(["Opis wiersza" + (i + 1), glownezrodlodanych[k][i]]);
    }
    doc.content.push({
        table: {
          widths: [90, 50],
          body: przedmioty
        }
      },
      '\n'
    );
  }

1 użytkowników online, w tym zalogowanych: 0, gości: 1