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
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

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
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
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";
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,
},
}));
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);
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);
};
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);
};
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>
);
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;
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
Dadurch wird der Entwicklungsserver gestartet, und Sie können die Anwendung in Ihrem Browser unter http://localhost:3000 anzeigen.

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.

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

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

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");
})();
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.




