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 Erstellung von PDFs erkunden und lernen, wie man die beliebte jsPDF-Bibliothek verwendet, um PDF-Dateien direkt aus Ihren 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.

Wahlen 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 weit verbreitete Bibliothek unter Entwicklern zur Erstellung 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, das Format und Layout Ihrer PDFs zu kontrollieren, von der Änderung der Schriftgröße und -farbe bis hin zur Anpassung der Seitenorientierung und -größe. jsPDF ist eine robuste Lösung, die sowohl in Browser- als auch in Serverumgebungen funktioniert, was es zu einer ausgezeichneten Wahl für eine Vielzahl von JavaScript-Anwendungen macht.

pdfmake

pdfmake sticht als Client/Server-seitige Lösung für das Drucken von PDFs in reinem JavaScript hervor. 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 Ihren Dokumenteninhalt und ihre Struktur mit einem einfachen JavaScript-Objekt definieren und dann in ein gültiges PDF-Dokument umwandeln.

React-PDF

React-PDF ist eine einzigartige Bibliothek, die leistungsstarke Funktionalitäten 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?

Während alle drei Bibliotheken leistungsstarke Werkzeuge zur Erstellung von PDF-Dokumenten in React bieten, werden wir in diesem Tutorial jsPDF aufgrund seiner Einfachheit, Flexibilität und breiten Akzeptanz in der Community verwenden. 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 erkunden werden, geben Ihnen eine solide Grundlage für die Erstellung von PDFs, und Sie werden in der Lage sein, andere Bibliotheken leichter zu verwenden, wenn Ihr Projekt dies erfordert.

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

Wie man eine PDF-Datei in React erstellt: Abbildung 1 - Ein Screenshot des Terminals, der den obigen Befehl in Bearbeitung 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 zum Erstellen von PDFs und Styling-Werkzeuge.

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, browserübergreifendes Verhalten zu verleihen, haben wir das styled-Werkzeug 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 App-Komponente. Wir haben vier Zustandsvariablen: customerName und customerAddress zur Verfolgung der Kundendaten, items zur Verfolgung der Liste der Artikel in der Rechnung und error, um eine Fehlermeldung bei Bedarf 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 Artikels, wenn ein Benutzer sie ändert. Die Funktion addItem fügt der Liste einen neuen Artikel hinzu. Die Funktion deleteItem entfernt einen Artikel 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

Im Folgenden befindet sich der Code der generateInvoice-Funktion:

// 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 gefüllt sind. Wenn eines dieser Felder leer ist, setzen wir den error-Zustand auf true und kehren frühzeitig zurück.

Als nächstes erstellen wir eine neue Instanz von jsPDF, indem wir new jsPDF("p", "pt") aufrufen. Das erste Argument "p" gibt die Seitenorientierung 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 setzen die Schriftgröße mit doc.setFontSize 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 dem Header fügen wir den Abschnitt "Items" hinzu, indem wir die Schriftgröße festlegen und mit doc.line eine Linie hinzufügen, um die Items vom Header zu trennen. Als nächstes iterieren wir über jeden Artikel im items-Array und berechnen den Gesamtpreis für jeden Artikel, indem wir die Menge mit dem Preis multiplizieren. Wir aktualisieren die Variable total mit der Summe aller Artikelgesamtpreise.

Für jeden Artikel verwenden wir doc.text, um den Artikelnamen, die Menge, den Preis und den Artikelgesamtpreis dem PDF-Dokument hinzuzufügen. Wir erhöhen die Variable yOffset, um für jeden Artikel zur nächsten Zeile zu wechseln. Schließlich fügen wir eine Linie hinzu, um die Artikel von der Summe zu trennen, und verwenden doc.text, um den Gesamtbetrag unten rechts im Dokument hinzuzufügen.

Sobald der Inhalt hinzugefügt wurde, verwenden wir doc.save("invoice.pdf"), um das erzeugte PDF als "invoice.pdf" auf dem Computer des Benutzers zu speichern. Schließlich setzen wir den error-Zustand auf false zurück, um frühere Validierungsfehler zu löschen.

Schritt 4: Rendering der Benutzeroberfläche

Die return-Anweisung 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 Button, TextField, Box, Container, Typography, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, IconButton, Snackbar und Alert, um grundlegende Komponenten 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

Dies wird den Entwicklungsserver starten, und Sie können die Anwendung in Ihrem Browser unter http://localhost:3000 ansehen.

Wie man eine PDF-Datei in React erstellt - Abbildung 2: Die Abgeschlossene Rechnungsanwendung mit den standardmäßig, nicht ausgefüllten 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.

Wie man eine PDF-Datei in React erstellt - Abbildung 3: Die Anwendung mit drei ausgefüllten Zeilenposten, mit unterschiedlichen Artikeln, Mengen und Preisen.

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

Wie man eine PDF-Datei in React erstellt - Abbildung 4: Die erstellte PDF.

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

Wie man eine PDF-Datei in React erstellt - Abbildung 5: Eine Fehlermeldung wird 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.

Here is an example of generating a PDF document from an HTML File, HTML String, and 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 erkundet, wie jsPDF, pdfmake und React-PDF, jede mit ihren eigenen Stärken und einzigartigen Merkmalen.

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. It is also available for other languages like C# .NET, Java, and Python. 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