Zum Fußzeileninhalt springen
PDF-WERKZEUGE

Wie man eine PDF-Datei in React erstellt

Willkommen beim Tutorial zur Erstellung von PDF-Dokumenten aus einer React-Anwendung! In diesem Tutorial werden wir verschiedene Bibliotheken zur PDF-Erstellung untersuchen und lernen, wie man die beliebte jsPDF Bibliothek verwendet, um PDF-Dateien direkt aus React-Komponenten zu erstellen. Lassen Sie uns eintauchen und loslegen!

PDF (Portable Document Format) ist ein weit verbreitetes Dateiformat zum Teilen und Drucken von Dokumenten, während ihr Layout und ihre Formatierung erhalten bleiben. Als React-Webentwickler könnten Sie auf Szenarien stoßen, in denen Sie PDF-Dokumente erstellen müssen, wie z. B. Rechnungen, Berichte oder Verkaufsverträge, direkt aus Ihrer React-Anwendung.

Auswahl einer React PDF-Bibliothek

PDF-Dokumente in einer React-Anwendung zu erstellen, kann eine herausfordernde Aufgabe sein, besonders wenn Sie neu in dieser Umgebung sind. Glücklicherweise stehen uns mehrere Drittanbieter-Bibliotheken zur Verfügung, die diesen Prozess erheblich vereinfachen. Jede Bibliothek hat ihre eigenen einzigartigen Merkmale und Werkzeuge, die auf unterschiedliche Anwendungsfälle abzielen. Lassen Sie uns diese Bibliotheken etwas genauer betrachten.

jsPDF

jsPDF ist eine bei Entwicklern weit verbreitete Bibliothek zur Generierung von PDF-Dateien aus JavaScript. Einer der Hauptvorteile ist seine Einfachheit. Seine Syntax und Verwendung sind recht unkompliziert, sodass Sie Ihre HTML-Inhalte im Handumdrehen in eine PDF-Datei umwandeln können.

Es ermöglicht Ihnen die Kontrolle über Formatierung und Layout Ihrer PDFs, von der Änderung der Schriftgröße und -farbe bis hin zur Anpassung von Seitenausrichtung und -größe. jsPDF ist eine robuste Lösung, die sowohl in Browser- als auch in Serverumgebungen funktioniert und sich daher hervorragend für eine Vielzahl von JavaScript Anwendungen eignet.

pdfmake

pdfmake zeichnet sich als client-/serverseitige PDF-Drucklösung in reinem JavaScript aus. Diese Bibliothek ist eine ausgezeichnete Wahl für die Erstellung komplexerer PDFs, dank ihrer umfassenden API und flexiblen Layout-Optionen. Mit pdfmake können Sie den Inhalt und die Struktur Ihres Dokuments mithilfe eines einfachen JavaScript Objekts definieren und es anschließend in ein gültiges PDF-Dokument umwandeln.

React-PDF

React-PDF ist eine einzigartige Bibliothek, die leistungsstarke Funktionen zur Erstellung von PDF-Dateien mit React-Komponenten bietet. Anstatt Ihre Dokumentstruktur manuell in einem JavaScript-Objekt zu schreiben, können Sie Ihr PDF erstellen, wie Sie eine typische React-Anwendung entwickeln - mit wiederverwendbaren Komponenten und Props. Die IronPDF-Website bietet ein Tutorial zur Erstellung von PDFs mit der React-PDF-Bibliothek.

Warum jsPDF wählen?

Obwohl alle drei Bibliotheken leistungsstarke Werkzeuge zur Generierung von PDF-Dokumenten in React bieten, werden wir in diesem Tutorial jsPDF verwenden, da sie einfach, flexibel und in der Community weit verbreitet ist. Es bietet einen niedrigeren Einstieg für Anfänger, und sein robustes Funktionsset macht es zu einer geeigneten Wahl für viele Anwendungsfälle. Die Prinzipien, die wir mit jsPDF untersuchen werden, werden Ihnen eine solide Grundlage für die PDF-Erstellung bieten, und Sie werden in der Lage sein, bei Bedarf weitere Bibliotheken leichter zu integrieren.

Voraussetzungen

Bevor wir in dieses Tutorial eintauchen, ist es wichtig, sicherzustellen, dass Sie mit den notwendigen Werkzeugen und dem Wissen ausgestattet sind, um reibungslos mitzukommen. Die Voraussetzungen für dieses Tutorial sind wie folgt:

Grundlegendes Verständnis von React

Zuallererst sollten Sie ein grundlegendes Verständnis von React haben, einer beliebten JavaScript-Bibliothek zum Erstellen von Benutzeroberflächen, insbesondere von Single-Page-Anwendungen. Sie sollten mit Konzepten wie JSX (JavaScript XML), Komponenten, Zustand und Props in React vertraut sein.

Entwicklungsumgebung

Sie sollten auch eine Entwicklungsumgebung auf Ihrem Computer eingerichtet haben, um React-Anwendungen zu erstellen. Dazu gehört ein Texteditor oder eine integrierte Entwicklungsumgebung (IDE). Texteditoren wie Visual Studio Code, Atom oder Sublime Text sind alles gute Optionen.

Node.js und npm

Zur Verwaltung unseres Projekts und seiner Abhängigkeiten verwenden wir Node.js und npm (Node Package Manager). Stellen Sie sicher, dass Sie Node.js auf Ihrem Computer installiert haben. Node.js ist eine JavaScript-Laufzeitumgebung, die es uns ermöglicht, JavaScript auf unseren Servern auszuführen. Es enthält npm, sodass Sie die für Ihr Projekt benötigten Bibliotheken verwalten können.

Sie können überprüfen, ob Node.js und npm installiert sind, indem Sie die folgenden Terminalbefehle ausführen:

node -v
npm -v
node -v
npm -v
SHELL

Diese Befehle zeigen die auf Ihrem System installierte Version von Node.js bzw. npm an. Wenn Sie sie nicht installiert haben oder Ihre Versionen veraltet sind, sollten Sie die neueste Long Term Support (LTS)-Version von Node.js von ihrer offiziellen Download-Seite herunterladen und installieren.

Schritt 1: Einrichten des Projekts

Beginnen wir mit der Einrichtung unseres React-Projekts. Öffnen Sie Ihr Terminal und navigieren Sie zu dem gewünschten Verzeichnis, in dem Sie Ihr Projekt erstellen möchten. Führen Sie den folgenden Befehl aus, um eine neue React-Anwendung zu erstellen:

npx create-react-app pdf-from-react
npx create-react-app pdf-from-react
SHELL

So erstellen Sie eine PDF-Datei in React: Abbildung 1 – Ein Screenshot des Terminals, der die Ausführung des obigen Befehls zeigt.

Dieser Befehl erstellt ein neues Verzeichnis namens pdf-from-react mit einer grundlegenden React-Projektstruktur.

Wechseln Sie als nächstes in das Verzeichnis des Projekts:

cd pdf-from-react
cd pdf-from-react
SHELL

Nun können wir das Projekt in unserem Code-Editor öffnen und mit der Implementierung fortfahren.

Schritt 2: Hinzufügen der erforderlichen Abhängigkeiten

Zuerst müssen wir die erforderlichen Pakete installieren. Installieren Sie react, react-dom, @mui/material und jspdf mit dem folgenden Terminalbefehl.

npm install jspdf @mui/material @emotion/react @emotion/styled @mui/icons-material
npm install jspdf @mui/material @emotion/react @emotion/styled @mui/icons-material
SHELL

Schritt 3: Gestaltung der PDF-Erstellungsfunktion

Importieren von Bibliotheken

Wir beginnen damit, die notwendigen Abhängigkeiten für unsere Anwendung zu importieren. Dazu gehören verschiedene Komponenten aus der Material-UI-Bibliothek, die jsPDF-Bibliothek zur PDF-Generierung sowie Styling-Hilfsprogramme.

import React, { useState } from "react";
import "./App.css";
import {
  Button,
  TextField,
  Box,
  Container,
  Typography,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TableRow,
  Paper,
  IconButton,
  Snackbar,
  Alert,
} from "@mui/material";
import Grid from "@mui/material/Grid";
import DeleteIcon from "@mui/icons-material/Delete";
import jsPDF from "jspdf";
import { styled } from "@mui/material/styles";
import { tableCellClasses } from "@mui/material/TableCell";
import React, { useState } from "react";
import "./App.css";
import {
  Button,
  TextField,
  Box,
  Container,
  Typography,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TableRow,
  Paper,
  IconButton,
  Snackbar,
  Alert,
} from "@mui/material";
import Grid from "@mui/material/Grid";
import DeleteIcon from "@mui/icons-material/Delete";
import jsPDF from "jspdf";
import { styled } from "@mui/material/styles";
import { tableCellClasses } from "@mui/material/TableCell";
JAVASCRIPT

Erstellen von gestylten Komponenten

Um unserer App ein einheitliches Verhalten über verschiedene Browser hinweg zu verleihen, haben wir das styled-Dienstprogramm aus der MUI-Bibliothek verwendet, um StyledTableCell und StyledTableRow zu erstellen.

const StyledTableCell = styled(TableCell)(({ theme }) => ({
  [`&.${tableCellClasses.head}`]: {
    backgroundColor: theme.palette.common.black,
    color: theme.palette.common.white,
  },
  [`&.${tableCellClasses.body}`]: {
    fontSize: 14,
  },
}));

const StyledTableRow = styled(TableRow)(({ theme }) => ({
  "&:nth-of-type(odd)": {
    backgroundColor: theme.palette.action.hover,
  },
  "&:last-child td, &:last-child th": {
    border: 0,
  },
}));
const StyledTableCell = styled(TableCell)(({ theme }) => ({
  [`&.${tableCellClasses.head}`]: {
    backgroundColor: theme.palette.common.black,
    color: theme.palette.common.white,
  },
  [`&.${tableCellClasses.body}`]: {
    fontSize: 14,
  },
}));

const StyledTableRow = styled(TableRow)(({ theme }) => ({
  "&:nth-of-type(odd)": {
    backgroundColor: theme.palette.action.hover,
  },
  "&:last-child td, &:last-child th": {
    border: 0,
  },
}));
JAVASCRIPT

Erstellen der App-Komponente

Die Hauptkomponente unserer Anwendung ist die Komponente App. Wir haben vier Zustandsvariablen: customerName und customerAddress, um die Kundendaten zu verwalten, items, um die Liste der Artikel in der Rechnung zu verwalten, und error, um bei Bedarf eine Fehlermeldung anzuzeigen.

function App() {
  // State variables
  const [customerName, setCustomerName] = useState("");
  const [customerAddress, setCustomerAddress] = useState("");
  const [items, setItems] = useState([{ name: "", quantity: "", price: "" }]);
  const [error, setError] = useState(false);
function App() {
  // State variables
  const [customerName, setCustomerName] = useState("");
  const [customerAddress, setCustomerAddress] = useState("");
  const [items, setItems] = useState([{ name: "", quantity: "", price: "" }]);
  const [error, setError] = useState(false);
JAVASCRIPT

Verarbeiten von Benutzereingaben

In diesem Codeblock haben wir Funktionen definiert, um Benutzerinteraktionen zu verarbeiten: Ändern von Artikeldetails, Hinzufügen eines neuen Artikels und Löschen eines Artikels. Die Funktion handleItemChange aktualisiert die Eigenschaften eines Elements, wenn ein Benutzer diese ändert. Die Funktion addItem fügt ein neues Element zur Liste hinzu. Die Funktion deleteItem entfernt ein Element aus der Liste.

const handleItemChange = (index, event) => {
  let newItems = [...items];
  newItems[index][event.target.name] = event.target.value;
  setItems(newItems);
};

const addItem = () => {
  setItems([...items, { name: "", quantity: "", price: "" }]);
};

const deleteItem = (index) => {
  let newItems = [...items];
  newItems.splice(index, 1);
  setItems(newItems);
};
const handleItemChange = (index, event) => {
  let newItems = [...items];
  newItems[index][event.target.name] = event.target.value;
  setItems(newItems);
};

const addItem = () => {
  setItems([...items, { name: "", quantity: "", price: "" }]);
};

const deleteItem = (index) => {
  let newItems = [...items];
  newItems.splice(index, 1);
  setItems(newItems);
};
JAVASCRIPT

Erstellen der Rechnung

Nachfolgend der Funktionscode generateInvoice:

// Generate invoice
const generateInvoice = () => {
  // Validate the input fields
  if (
    !customerName ||
    !customerAddress ||
    items.some((item) => !item.name || !item.quantity || !item.price)
  ) {
    setError(true);
    return;
  }

  // Create a new jsPDF instance
  let doc = new jsPDF("p", "pt");

  // Add invoice header
  doc.setFontSize(24);
  doc.text("Invoice", 40, 60);
  doc.setFontSize(10);
  doc.text("Invoice Number: 123456", 40, 90);
  doc.text("Date: " + new Date().toDateString(), 40, 110);
  doc.text(`Customer Name: ${customerName}`, 40, 130);
  doc.text(`Customer Address: ${customerAddress}`, 40, 150);

  // Add items section
  doc.setFontSize(14);
  doc.text("Items:", 40, 200);
  doc.line(40, 210, 550, 210);

  // Add item details
  doc.setFontSize(12);
  let yOffset = 240;
  let total = 0;

  items.forEach((item) => {
    let itemTotal = item.quantity * item.price;
    total += itemTotal;

    doc.text(`Item: ${item.name}`, 40, yOffset);
    doc.text(`Quantity: ${item.quantity}`, 200, yOffset);
    doc.text(`Price: $${item.price}`, 300, yOffset);
    doc.text(`Total: $${itemTotal}`, 400, yOffset);

    yOffset += 20;
  });

  // Add total
  doc.line(40, yOffset, 550, yOffset);
  doc.setFontSize(14);
  doc.text(`Total: $${total}`, 400, yOffset + 30);

  // Save the generated PDF as "invoice.pdf"
  doc.save("invoice.pdf");

  // Reset error state
  setError(false);
};
// Generate invoice
const generateInvoice = () => {
  // Validate the input fields
  if (
    !customerName ||
    !customerAddress ||
    items.some((item) => !item.name || !item.quantity || !item.price)
  ) {
    setError(true);
    return;
  }

  // Create a new jsPDF instance
  let doc = new jsPDF("p", "pt");

  // Add invoice header
  doc.setFontSize(24);
  doc.text("Invoice", 40, 60);
  doc.setFontSize(10);
  doc.text("Invoice Number: 123456", 40, 90);
  doc.text("Date: " + new Date().toDateString(), 40, 110);
  doc.text(`Customer Name: ${customerName}`, 40, 130);
  doc.text(`Customer Address: ${customerAddress}`, 40, 150);

  // Add items section
  doc.setFontSize(14);
  doc.text("Items:", 40, 200);
  doc.line(40, 210, 550, 210);

  // Add item details
  doc.setFontSize(12);
  let yOffset = 240;
  let total = 0;

  items.forEach((item) => {
    let itemTotal = item.quantity * item.price;
    total += itemTotal;

    doc.text(`Item: ${item.name}`, 40, yOffset);
    doc.text(`Quantity: ${item.quantity}`, 200, yOffset);
    doc.text(`Price: $${item.price}`, 300, yOffset);
    doc.text(`Total: $${itemTotal}`, 400, yOffset);

    yOffset += 20;
  });

  // Add total
  doc.line(40, yOffset, 550, yOffset);
  doc.setFontSize(14);
  doc.text(`Total: $${total}`, 400, yOffset + 30);

  // Save the generated PDF as "invoice.pdf"
  doc.save("invoice.pdf");

  // Reset error state
  setError(false);
};
JAVASCRIPT

In der Funktion generateInvoice führen wir zunächst eine Validierung der Eingabefelder durch, um sicherzustellen, dass der Kundenname, die Kundenadresse und die Artikeldetails ausgefüllt sind. Falls eines dieser Felder leer ist, setzen wir den Zustand error auf true und kehren vorzeitig zurück.

Als nächstes erstellen wir eine neue Instanz von jsPDF durch Aufruf von new jsPDF("p", "pt"). Das erste Argument "p" gibt die Seitenausrichtung als Hochformat an, und das zweite Argument "pt"" gibt die Maßeinheit als Punkte an.

Wir beginnen dann mit dem Hinzufügen von Inhalten zu unserem PDF-Dokument. Wir legen die Schriftgröße mit doc.setFontSize fest und verwenden die Methode doc.text, um Text an bestimmten Koordinaten auf der Seite hinzuzufügen. Wir fügen den Rechnungsheader hinzu, einschließlich Titel, Rechnungsnummer, Datum, Kundenname und Kundenadresse.

Nach der Kopfzeile fügen wir den Abschnitt "Items" hinzu, indem wir die Schriftgröße festlegen und eine Linie mit doc.line einfügen, um die Items von der Kopfzeile zu trennen. Anschließend durchlaufen wir jedes Element im Array items und berechnen den Gesamtpreis jedes Elements, indem wir die Menge mit dem Preis multiplizieren. Die Variable total wird mit der Summe aller Elementgesamtpreise aktualisiert.

Für jeden Artikel verwenden wir doc.text, um den Artikelnamen, die Menge, den Preis und die Artikelsumme in das PDF-Dokument einzufügen. Wir erhöhen die Variable yOffset, um für jedes Element zur nächsten Zeile zu gelangen. Zum Schluss fügen wir eine Linie ein, um die einzelnen Positionen von der Gesamtsumme zu trennen, und verwenden doc.text, um den Gesamtbetrag unten rechts im Dokument hinzuzufügen.

Sobald die Inhalte hinzugefügt wurden, verwenden wir doc.save("invoice.pdf"), um die generierte PDF-Datei als "invoice.pdf" auf dem Computer des Benutzers zu speichern. Zum Schluss haben wir den Zustand error auf false zurückgesetzt, um alle vorherigen Validierungsfehler zu beheben.

Schritt 4: UI-Rendering

Die Anweisung return enthält den JSX-Code, der den Rendering-Prozess steuert. Es enthält Eingabefelder für den Kundennamen und die Adresse, eine Tabelle zum Eingeben von Artikeldetails, Schaltflächen zum Hinzufügen von Artikeln und zum Erstellen der Rechnung sowie ein Fehler-Snackbar zum Anzeigen von Validierungsfehlern.

Es verwendet Komponenten aus der Material-UI-Bibliothek, wie z. B. Button, TextField, Box, Container, Typography, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, IconButton, Snackbar, und Alert, um Basiskomponenten zu erstellen. Diese Komponenten werden verwendet, um die Formularelemente, Tabellen, Schaltflächen und Fehlermeldungen zu erstellen.

return (
  <Container maxWidth="md">
    <Box sx={{ my: 4 }}>
      <Typography variant="h3" component="h1" gutterBottom>
        Create Invoice
      </Typography>

      {/* Customer Name and Address fields */}
      <Grid container spacing={3}>
        <Grid item xs={6}>
          <TextField
            label="Customer Name"
            fullWidth
            margin="normal"
            value={customerName}
            onChange={(e) => setCustomerName(e.target.value)}
          />
        </Grid>
        <Grid item xs={6}>
          <TextField
            label="Customer Address"
            fullWidth
            margin="normal"
            value={customerAddress}
            onChange={(e) => setCustomerAddress(e.target.value)}
          />
        </Grid>
      </Grid>

      {/* Items table */}
      <TableContainer component={Paper}>
        <Table sx={{ minWidth: 700 }} aria-label="invoice table">
          <TableHead>
            <TableRow>
              <StyledTableCell>Item Name</StyledTableCell>
              <StyledTableCell align="left">Quantity</StyledTableCell>
              <StyledTableCell align="left">Price</StyledTableCell>
              <StyledTableCell align="left">Action</StyledTableCell>
            </TableRow>
          </TableHead>
          <TableBody>
            {items.map((item, index) => (
              <StyledTableRow key={index}>
                <StyledTableCell component="th" scope="row">
                  <TextField
                    fullWidth
                    value={item.name}
                    onChange={(event) => handleItemChange(index, event)}
                    name="name"
                  />
                </StyledTableCell>
                <StyledTableCell align="right">
                  <TextField
                    fullWidth
                    value={item.quantity}
                    onChange={(event) => handleItemChange(index, event)}
                    name="quantity"
                  />
                </StyledTableCell>
                <StyledTableCell align="right">
                  <TextField
                    fullWidth
                    value={item.price}
                    onChange={(event) => handleItemChange(index, event)}
                    name="price"
                  />
                </StyledTableCell>
                <StyledTableCell align="right">
                  <IconButton onClick={() => deleteItem(index)}>
                    <DeleteIcon />
                  </IconButton>
                </StyledTableCell>
              </StyledTableRow>
            ))}
          </TableBody>
        </Table>
      </TableContainer>

      {/* Buttons */}
      <Box mt={2} display="flex" gap={2}>
        <Button variant="contained" onClick={addItem}>
          Add Item
        </Button>
        <Button variant="outlined" color="success" onClick={generateInvoice}>
          Generate Invoice
        </Button>
      </Box>
    </Box>

    {/* Error Snackbar */}
    <Snackbar
      open={error}
      autoHideDuration={6000}
      onClose={() => setError(false)}
      anchorOrigin={{ vertical: "top", horizontal: "right" }}
    >
      <Alert onClose={() => setError(false)} severity="error">
        Please fill in all required fields.
      </Alert>
    </Snackbar>
  </Container>
);
return (
  <Container maxWidth="md">
    <Box sx={{ my: 4 }}>
      <Typography variant="h3" component="h1" gutterBottom>
        Create Invoice
      </Typography>

      {/* Customer Name and Address fields */}
      <Grid container spacing={3}>
        <Grid item xs={6}>
          <TextField
            label="Customer Name"
            fullWidth
            margin="normal"
            value={customerName}
            onChange={(e) => setCustomerName(e.target.value)}
          />
        </Grid>
        <Grid item xs={6}>
          <TextField
            label="Customer Address"
            fullWidth
            margin="normal"
            value={customerAddress}
            onChange={(e) => setCustomerAddress(e.target.value)}
          />
        </Grid>
      </Grid>

      {/* Items table */}
      <TableContainer component={Paper}>
        <Table sx={{ minWidth: 700 }} aria-label="invoice table">
          <TableHead>
            <TableRow>
              <StyledTableCell>Item Name</StyledTableCell>
              <StyledTableCell align="left">Quantity</StyledTableCell>
              <StyledTableCell align="left">Price</StyledTableCell>
              <StyledTableCell align="left">Action</StyledTableCell>
            </TableRow>
          </TableHead>
          <TableBody>
            {items.map((item, index) => (
              <StyledTableRow key={index}>
                <StyledTableCell component="th" scope="row">
                  <TextField
                    fullWidth
                    value={item.name}
                    onChange={(event) => handleItemChange(index, event)}
                    name="name"
                  />
                </StyledTableCell>
                <StyledTableCell align="right">
                  <TextField
                    fullWidth
                    value={item.quantity}
                    onChange={(event) => handleItemChange(index, event)}
                    name="quantity"
                  />
                </StyledTableCell>
                <StyledTableCell align="right">
                  <TextField
                    fullWidth
                    value={item.price}
                    onChange={(event) => handleItemChange(index, event)}
                    name="price"
                  />
                </StyledTableCell>
                <StyledTableCell align="right">
                  <IconButton onClick={() => deleteItem(index)}>
                    <DeleteIcon />
                  </IconButton>
                </StyledTableCell>
              </StyledTableRow>
            ))}
          </TableBody>
        </Table>
      </TableContainer>

      {/* Buttons */}
      <Box mt={2} display="flex" gap={2}>
        <Button variant="contained" onClick={addItem}>
          Add Item
        </Button>
        <Button variant="outlined" color="success" onClick={generateInvoice}>
          Generate Invoice
        </Button>
      </Box>
    </Box>

    {/* Error Snackbar */}
    <Snackbar
      open={error}
      autoHideDuration={6000}
      onClose={() => setError(false)}
      anchorOrigin={{ vertical: "top", horizontal: "right" }}
    >
      <Alert onClose={() => setError(false)} severity="error">
        Please fill in all required fields.
      </Alert>
    </Snackbar>
  </Container>
);
JAVASCRIPT

Vollständiger App.js- und App.css-Code

Hier ist der vollständige App.js Code, den Sie in Ihr Projekt kopieren und einfügen können:

import React, { useState } from "react";
import "./App.css";
import {
  Button,
  TextField,
  Box,
  Container,
  Typography,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TableRow,
  Paper,
  IconButton,
  Snackbar,
  Alert,
} from "@mui/material";
import Grid from "@mui/material/Grid";
import DeleteIcon from "@mui/icons-material/Delete";
import jsPDF from "jspdf";
import { styled } from "@mui/material/styles";
import { tableCellClasses } from "@mui/material/TableCell";

const StyledTableCell = styled(TableCell)(({ theme }) => ({
  [`&.${tableCellClasses.head}`]: {
    backgroundColor: theme.palette.common.black,
    color: theme.palette.common.white,
  },
  [`&.${tableCellClasses.body}`]: {
    fontSize: 14,
  },
}));

const StyledTableRow = styled(TableRow)(({ theme }) => ({
  "&:nth-of-type(odd)": {
    backgroundColor: theme.palette.action.hover,
  },
  "&:last-child td, &:last-child th": {
    border: 0,
  },
}));

function App() {
  // State variables
  const [customerName, setCustomerName] = useState("");
  const [customerAddress, setCustomerAddress] = useState("");
  const [items, setItems] = useState([{ name: "", quantity: "", price: "" }]);
  const [error, setError] = useState(false);

  // Event handler for item changes
  const handleItemChange = (index, event) => {
    let newItems = [...items];
    newItems[index][event.target.name] = event.target.value;
    setItems(newItems);
  };

  // Add new item to the list
  const addItem = () => {
    setItems([...items, { name: "", quantity: "", price: "" }]);
  };

  // Delete an item from the list
  const deleteItem = (index) => {
    let newItems = [...items];
    newItems.splice(index, 1);
    setItems(newItems);
  };

  // Generate invoice
  const generateInvoice = () => {
    // Validate the input fields
    if (
      !customerName ||
      !customerAddress ||
      items.some((item) => !item.name || !item.quantity || !item.price)
    ) {
      setError(true);
      return;
    }

    // Create a new jsPDF instance
    let doc = new jsPDF("p", "pt");

    // Add invoice header
    doc.setFontSize(24);
    doc.text("Invoice", 40, 60);
    doc.setFontSize(10);
    doc.text("Invoice Number: 123456", 40, 90);
    doc.text("Date: " + new Date().toDateString(), 40, 110);
    doc.text(`Customer Name: ${customerName}`, 40, 130);
    doc.text(`Customer Address: ${customerAddress}`, 40, 150);

    // Add items section
    doc.setFontSize(14);
    doc.text("Items:", 40, 200);
    doc.line(40, 210, 550, 210);

    // Add item details
    doc.setFontSize(12);
    let yOffset = 240;
    let total = 0;

    items.forEach((item) => {
      let itemTotal = item.quantity * item.price;
      total += itemTotal;

      doc.text(`Item: ${item.name}`, 40, yOffset);
      doc.text(`Quantity: ${item.quantity}`, 200, yOffset);
      doc.text(`Price: $${item.price}`, 300, yOffset);
      doc.text(`Total: $${itemTotal}`, 400, yOffset);

      yOffset += 20;
    });

    // Add total
    doc.line(40, yOffset, 550, yOffset);
    doc.setFontSize(14);
    doc.text(`Total: $${total}`, 400, yOffset + 30);

    // Save the generated PDF as "invoice.pdf"
    doc.save("invoice.pdf");

    // Reset error state
    setError(false);
  };

  return (
    <Container maxWidth="md">
      <Box sx={{ my: 4 }}>
        <Typography variant="h3" component="h1" gutterBottom>
          Create Invoice
        </Typography>

        {/* Customer Name and Address fields */}
        <Grid container spacing={3}>
          <Grid item xs={6}>
            <TextField
              label="Customer Name"
              fullWidth
              margin="normal"
              value={customerName}
              onChange={(e) => setCustomerName(e.target.value)}
            />
          </Grid>
          <Grid item xs={6}>
            <TextField
              label="Customer Address"
              fullWidth
              margin="normal"
              value={customerAddress}
              onChange={(e) => setCustomerAddress(e.target.value)}
            />
          </Grid>
        </Grid>

        {/* Items table */}
        <TableContainer component={Paper}>
          <Table sx={{ minWidth: 700 }} aria-label="invoice table">
            <TableHead>
              <TableRow>
                <StyledTableCell>Item Name</StyledTableCell>
                <StyledTableCell align="left">Quantity</StyledTableCell>
                <StyledTableCell align="left">Price</StyledTableCell>
                <StyledTableCell align="left">Action</StyledTableCell>
              </TableRow>
            </TableHead>
            <TableBody>
              {items.map((item, index) => (
                <StyledTableRow key={index}>
                  <StyledTableCell component="th" scope="row">
                    <TextField
                      fullWidth
                      value={item.name}
                      onChange={(event) => handleItemChange(index, event)}
                      name="name"
                    />
                  </StyledTableCell>
                  <StyledTableCell align="right">
                    <TextField
                      fullWidth
                      value={item.quantity}
                      onChange={(event) => handleItemChange(index, event)}
                      name="quantity"
                    />
                  </StyledTableCell>
                  <StyledTableCell align="right">
                    <TextField
                      fullWidth
                      value={item.price}
                      onChange={(event) => handleItemChange(index, event)}
                      name="price"
                    />
                  </StyledTableCell>
                  <StyledTableCell align="right">
                    <IconButton onClick={() => deleteItem(index)}>
                      <DeleteIcon />
                    </IconButton>
                  </StyledTableCell>
                </StyledTableRow>
              ))}
            </TableBody>
          </Table>
        </TableContainer>

        {/* Buttons */}
        <Box mt={2} display="flex" gap={2}>
          <Button variant="contained" onClick={addItem}>
            Add Item
          </Button>
          <Button variant="outlined" color="success" onClick={generateInvoice}>
            Generate Invoice
          </Button>
        </Box>
      </Box>

      {/* Error Snackbar */}
      <Snackbar
        open={error}
        autoHideDuration={6000}
        onClose={() => setError(false)}
        anchorOrigin={{ vertical: "top", horizontal: "right" }}
      >
        <Alert onClose={() => setError(false)} severity="error">
          Please fill in all required fields.
        </Alert>
      </Snackbar>
    </Container>
  );
}
export default App;
import React, { useState } from "react";
import "./App.css";
import {
  Button,
  TextField,
  Box,
  Container,
  Typography,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TableRow,
  Paper,
  IconButton,
  Snackbar,
  Alert,
} from "@mui/material";
import Grid from "@mui/material/Grid";
import DeleteIcon from "@mui/icons-material/Delete";
import jsPDF from "jspdf";
import { styled } from "@mui/material/styles";
import { tableCellClasses } from "@mui/material/TableCell";

const StyledTableCell = styled(TableCell)(({ theme }) => ({
  [`&.${tableCellClasses.head}`]: {
    backgroundColor: theme.palette.common.black,
    color: theme.palette.common.white,
  },
  [`&.${tableCellClasses.body}`]: {
    fontSize: 14,
  },
}));

const StyledTableRow = styled(TableRow)(({ theme }) => ({
  "&:nth-of-type(odd)": {
    backgroundColor: theme.palette.action.hover,
  },
  "&:last-child td, &:last-child th": {
    border: 0,
  },
}));

function App() {
  // State variables
  const [customerName, setCustomerName] = useState("");
  const [customerAddress, setCustomerAddress] = useState("");
  const [items, setItems] = useState([{ name: "", quantity: "", price: "" }]);
  const [error, setError] = useState(false);

  // Event handler for item changes
  const handleItemChange = (index, event) => {
    let newItems = [...items];
    newItems[index][event.target.name] = event.target.value;
    setItems(newItems);
  };

  // Add new item to the list
  const addItem = () => {
    setItems([...items, { name: "", quantity: "", price: "" }]);
  };

  // Delete an item from the list
  const deleteItem = (index) => {
    let newItems = [...items];
    newItems.splice(index, 1);
    setItems(newItems);
  };

  // Generate invoice
  const generateInvoice = () => {
    // Validate the input fields
    if (
      !customerName ||
      !customerAddress ||
      items.some((item) => !item.name || !item.quantity || !item.price)
    ) {
      setError(true);
      return;
    }

    // Create a new jsPDF instance
    let doc = new jsPDF("p", "pt");

    // Add invoice header
    doc.setFontSize(24);
    doc.text("Invoice", 40, 60);
    doc.setFontSize(10);
    doc.text("Invoice Number: 123456", 40, 90);
    doc.text("Date: " + new Date().toDateString(), 40, 110);
    doc.text(`Customer Name: ${customerName}`, 40, 130);
    doc.text(`Customer Address: ${customerAddress}`, 40, 150);

    // Add items section
    doc.setFontSize(14);
    doc.text("Items:", 40, 200);
    doc.line(40, 210, 550, 210);

    // Add item details
    doc.setFontSize(12);
    let yOffset = 240;
    let total = 0;

    items.forEach((item) => {
      let itemTotal = item.quantity * item.price;
      total += itemTotal;

      doc.text(`Item: ${item.name}`, 40, yOffset);
      doc.text(`Quantity: ${item.quantity}`, 200, yOffset);
      doc.text(`Price: $${item.price}`, 300, yOffset);
      doc.text(`Total: $${itemTotal}`, 400, yOffset);

      yOffset += 20;
    });

    // Add total
    doc.line(40, yOffset, 550, yOffset);
    doc.setFontSize(14);
    doc.text(`Total: $${total}`, 400, yOffset + 30);

    // Save the generated PDF as "invoice.pdf"
    doc.save("invoice.pdf");

    // Reset error state
    setError(false);
  };

  return (
    <Container maxWidth="md">
      <Box sx={{ my: 4 }}>
        <Typography variant="h3" component="h1" gutterBottom>
          Create Invoice
        </Typography>

        {/* Customer Name and Address fields */}
        <Grid container spacing={3}>
          <Grid item xs={6}>
            <TextField
              label="Customer Name"
              fullWidth
              margin="normal"
              value={customerName}
              onChange={(e) => setCustomerName(e.target.value)}
            />
          </Grid>
          <Grid item xs={6}>
            <TextField
              label="Customer Address"
              fullWidth
              margin="normal"
              value={customerAddress}
              onChange={(e) => setCustomerAddress(e.target.value)}
            />
          </Grid>
        </Grid>

        {/* Items table */}
        <TableContainer component={Paper}>
          <Table sx={{ minWidth: 700 }} aria-label="invoice table">
            <TableHead>
              <TableRow>
                <StyledTableCell>Item Name</StyledTableCell>
                <StyledTableCell align="left">Quantity</StyledTableCell>
                <StyledTableCell align="left">Price</StyledTableCell>
                <StyledTableCell align="left">Action</StyledTableCell>
              </TableRow>
            </TableHead>
            <TableBody>
              {items.map((item, index) => (
                <StyledTableRow key={index}>
                  <StyledTableCell component="th" scope="row">
                    <TextField
                      fullWidth
                      value={item.name}
                      onChange={(event) => handleItemChange(index, event)}
                      name="name"
                    />
                  </StyledTableCell>
                  <StyledTableCell align="right">
                    <TextField
                      fullWidth
                      value={item.quantity}
                      onChange={(event) => handleItemChange(index, event)}
                      name="quantity"
                    />
                  </StyledTableCell>
                  <StyledTableCell align="right">
                    <TextField
                      fullWidth
                      value={item.price}
                      onChange={(event) => handleItemChange(index, event)}
                      name="price"
                    />
                  </StyledTableCell>
                  <StyledTableCell align="right">
                    <IconButton onClick={() => deleteItem(index)}>
                      <DeleteIcon />
                    </IconButton>
                  </StyledTableCell>
                </StyledTableRow>
              ))}
            </TableBody>
          </Table>
        </TableContainer>

        {/* Buttons */}
        <Box mt={2} display="flex" gap={2}>
          <Button variant="contained" onClick={addItem}>
            Add Item
          </Button>
          <Button variant="outlined" color="success" onClick={generateInvoice}>
            Generate Invoice
          </Button>
        </Box>
      </Box>

      {/* Error Snackbar */}
      <Snackbar
        open={error}
        autoHideDuration={6000}
        onClose={() => setError(false)}
        anchorOrigin={{ vertical: "top", horizontal: "right" }}
      >
        <Alert onClose={() => setError(false)} severity="error">
          Please fill in all required fields.
        </Alert>
      </Snackbar>
    </Container>
  );
}
export default App;
JAVASCRIPT

Hier ist der App.css Code:

@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap');
.App {
  text-align: center;
}

.App-logo {
  height: 40vmin;
  pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
  .App-logo {
    animation: App-logo-spin infinite 20s linear;
  }
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-weight: bold;
  /* This is the weight for bold in Poppins */
  color: #FF6347;
  /* This is the color Tomato. Replace with your preferred color */
}

body {
  font-family: 'Poppins', sans-serif;
  background-color: #E9F8F4;
}

.App-header {
  background-color: #282c34;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: calc(10px + 2vmin);
  color: white;
}

.App-link {
  color: #61dafb;
}

@keyframes App-logo-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

Schritt 5: Testen der Anwendung

Um die PDF-Erstellungsfunktion zu testen, führen Sie den folgenden Befehl im Terminal aus:

npm start
npm start
SHELL

Dadurch wird der Entwicklungsserver gestartet, und Sie können die Anwendung in Ihrem Browser unter http://localhost:3000 anzeigen.

So erstellen Sie eine PDF-Datei in React – Abbildung 2: Die fertige Rechnungsanwendung mit den standardmäßigen, leeren Feldern.

Füllen Sie die Kundenname, Adresse und Artikeldetails in die Eingabefelder ein und klicken Sie auf die Schaltfläche "Rechnung erstellen". Die PDF-Datei wird auf Ihren Computer heruntergeladen, und Sie können sie öffnen, um die Vollbildansicht der erzeugten Rechnung zu sehen.

So erstellen Sie eine PDF-Datei in React – Abbildung 3: Die Anwendung mit drei ausgefüllten Zeilen, die unterschiedliche Artikel, Mengen und Preise enthalten.

Wenn Sie auf die Schaltfläche "Rechnung erstellen" klicken, wird die PDF-Datei erstellt.

So erstellen Sie eine PDF-Datei in React – Abbildung 4: Die generierte PDF-Datei.

Wenn Sie versuchen, ein PDF zu erstellen, obwohl ein Feld leer ist, wird oben rechts eine Fehlermeldung angezeigt.

So erstellen Sie eine PDF-Datei in React – Abbildung 5: Es wird eine Fehlermeldung angezeigt, da nicht alle Felder ausgefüllt wurden.

IronPDF - Die Node.js PDF Bibliothek

IronPDF für Node.js ist eine umfassende Node.js-PDF-Bibliothek, die sich durch Genauigkeit, Benutzerfreundlichkeit und Geschwindigkeit auszeichnet. Es bietet eine Vielzahl von Funktionen zum Erstellen, Bearbeiten und Formatieren von PDFs direkt aus HTML, URLs und Bildern in React. Mit Unterstützung für verschiedene Plattformen, einschließlich Windows, MacOS, Linux, Docker und Cloud-Plattformen wie Azure und AWS, sorgt IronPDF für plattformübergreifende Kompatibilität. Sein benutzerfreundliches API ermöglicht es Entwicklern, die PDF-Erzeugung und -Manipulation schnell in ihre Node.js-Projekte zu integrieren.

Zu den bemerkenswerten Funktionen von IronPDF Node.js gehören: pixelgenaue Wiedergabe, umfangreiche Formatierungsoptionen und erweiterte Bearbeitungsmöglichkeiten wie das Zusammenführen und Aufteilen von PDFs, das Hinzufügen von Anmerkungen und das Erstellen von PDF-Formularen.

Hier ist ein Beispiel zur Erstellung eines PDF-Dokuments aus einer HTML-Datei, einem HTML-String und einer URL:

import { PdfDocument } from "@ironsoftware/ironpdf";

(async () => {
  const pdfFromUrl = await PdfDocument.fromUrl("https://getbootstrap.com/");
  await pdfFromUrl.saveAs("website.pdf");

  const pdfFromHtmlFile = await PdfDocument.fromHtml("design.html");
  await pdfFromHtmlFile.saveAs("markup.pdf");

  const pdfFromHtmlString = await PdfDocument.fromHtml("<p>Hello World</p>");
  await pdfFromHtmlString.saveAs("markup_with_assets.pdf");
})();
import { PdfDocument } from "@ironsoftware/ironpdf";

(async () => {
  const pdfFromUrl = await PdfDocument.fromUrl("https://getbootstrap.com/");
  await pdfFromUrl.saveAs("website.pdf");

  const pdfFromHtmlFile = await PdfDocument.fromHtml("design.html");
  await pdfFromHtmlFile.saveAs("markup.pdf");

  const pdfFromHtmlString = await PdfDocument.fromHtml("<p>Hello World</p>");
  await pdfFromHtmlString.saveAs("markup_with_assets.pdf");
})();
JAVASCRIPT

Für weitere Codebeispiele für PDF-bezogene Aufgaben besuchen Sie bitte diese IronPDF-Codebeispiele-Seite.

Abschluss

Abschließend muss das Erstellen von PDFs in einer React-Anwendung nicht einschüchternd sein. Mit den richtigen Werkzeugen und einem klaren Verständnis können Sie mühelos schöne, gut strukturierte PDF-Dokumente erzeugen. Wir haben verschiedene Bibliotheken wie jsPDF, pdfmake und React-PDF untersucht, von denen jede ihre eigenen Stärken und einzigartigen Merkmale aufweist.

Mit IronPDFs einfacher Integration in JavaScript-Frameworks und -Bibliotheken, hervorragender Dokumentation und reaktionsschnellem technischen Support können Entwickler im Handumdrehen loslegen, was es zu einer Top-Wahl für die Erstellung professioneller PDFs in Node.js-Anwendungen macht.

IronPDF bietet eine kostenlose Testversion mit vollem Funktionsumfang an. Es ist auch in anderen Sprachen wie C# .NET, Java und Python verfügbar. Besuchen Sie die IronPDF-Website für weitere Details.

Darrius Serrant
Full-Stack-Software-Ingenieur (WebOps)

Darrius Serrant hat einen Bachelor-Abschluss in Informatik von der University of Miami und arbeitet als Full-Stack-WebOps-Marketing-Ingenieur bei Iron Software. Seit seiner Jugend vom Programmieren angezogen, sah er die Informatik als sowohl mysteriös als auch zugänglich, was es zum perfekten Medium für Kreativität und Problemlösung ...

Weiterlesen

Iron Support Team

Wir sind 24 Stunden am Tag, 5 Tage die Woche online.
Chat
E-Mail
Rufen Sie mich an