Full-Stack-Webentwicklung mit Python und Reflex
·
Kursüberblick
1
Reflex kennenlernen und Umgebung einrichten
2
Die Oberfläche mit Komponenten zeichnen
3
State und Vars: Daten zeichnen die Oberfläche
4
Events und Formulare: Kunden hinzufügen
5
Daten aufbereiten und Diagramme
6
Zusammenfassung, Debugging und nächste Schritte
·
Anhang
Zeit 2:10–2:55
Am Ende dieser Lektion öffnet der Button „Add User“ einen Dialog. Sendest du das Formular ab, landet der Kunde in der Tabelle, und der Dialog schließt sich automatisch. Damit reagiert die Oberfläche zum ersten Mal auf das, was Nutzer tun, und genau das ist der wichtigste Schritt.
Ein Event-Handler ist eine Methode im State, die mit @rx.event dekoriert ist. Nur Event-Handler können den State ändern.
class State(rx.State):
users: list[User] = [...]
@rx.event
def add_user(self, form_data: dict):
self.users.append(User(**form_data))self.users keine Var, sondern eine richtige Liste. Du kannst also nach Belieben append, len, if, for und sogar externe Bibliotheken verwenden.self.users, erkennt Reflex die Änderung, schickt sie an den Browser, und Tabelle und Badge werden automatisch neu gezeichnet. Code, der die Oberfläche direkt anpasst, gibt es nicht.User(**form_data) entpackt das Dictionary und übergibt die Einträge als Keyword-Argumente: {"name": "Kim", "email": "...", "gender": "Male"} → User(name="Kim", email="...", gender="Male").Hinweis für Lehrende — Stell das der Tabelle aus Lektion 3 gegenüber: „In der Oberflächen-Funktion ist es eine Var, also kein len. Im Handler ist es ein echter Wert, also geht len.“ Wer diesen Gegensatz verstanden hat, hat die halbe Miete in Reflex.
rx.form(
rx.flex(
rx.input(placeholder="User Name", name="name", required=True),
rx.input(placeholder="user@reflex.dev", name="email"),
rx.select(
["Male", "Female"],
placeholder="Select gender",
default_value="Male",
name="gender",
custom_attrs={"aria-label": "Gender"},
),
rx.flex(
rx.dialog.close(
rx.button("Cancel", type="button", variant="soft", color_scheme="gray"),
),
rx.button("Submit", type="submit"),
spacing="3",
justify="end",
),
direction="column",
spacing="4",
),
on_submit=State.add_user,
reset_on_submit=False,
)name= ist der gemeinsame Schlüssel. Beim Absenden wird aus dem name jedes Eingabefelds ein Schlüssel im Dictionary form_data. Diese Namen müssen exakt den Feldnamen von User (name, email, gender) entsprechen, damit User(**form_data) funktioniert.on_submit=State.add_user übergibst du den Handler über die Klasse und ohne ihn aufzurufen. Das Dictionary aus dem Formular wird automatisch als form_data eingesetzt.required=True verhindert der Browser, dass das Formular mit leerem Feld abgeschickt wird.type="button" beim Cancel-Button. Buttons in einem Formular sind standardmäßig submit. Vergisst du das, wird auch beim Klick auf Abbrechen ein Kunde hinzugefügt.rx.flex ist eine Box mit display="flex". Mit direction="column" werden die Kinder untereinander angeordnet, spacing legt den Abstand fest.custom_attrs hängst du HTML-Attribute an, die Reflex nicht kennt. Hier ist es ein Label für Screenreader.def add_customer_button() -> rx.Component:
return rx.dialog.root(
rx.dialog.trigger( # Element, das beim Klick den Dialog öffnet
rx.button(
rx.icon("plus", size=18),
rx.text("Add User", size="2"),
),
),
rx.dialog.content( # Inhalt des Dialogs
rx.dialog.title("Add New User"),
rx.dialog.description("Fill the form with the user's info"),
rx.form(...), # das Formular aus 4-2
max_width="450px",
),
)rx.icon("plus") verwendest du direkt die Namen der Lucide-Icons.add_customer_button() auf der rechten Seite des Headers ein (nach der Titel-Box, im selben Elternelement).Im Browser prüfen — Ganz rechts im Header erscheint ein blauer Button „+ Add User“.
localhost:3000
AusprobierenYour customer directory and audience breakdown.
Klickst du auf den Button, wird der Hintergrund abgedunkelt und der Dialog erscheint; beim Geschlecht ist Male vorausgewählt. Gib in der Vorschau oben selbst Werte ein und klick auf Submit.
Wenn du den Stand bis hierhin ausführst, fällt dir ein Problem auf: Der Kunde wird zwar hinzugefügt, aber der Dialog schließt sich nicht. Ob der Dialog offen oder geschlossen ist, verwaltet der Browser ganz allein. Der Handler auf dem Server hat also keine Möglichkeit, ihn zu schließen.
Die Lösung: Wir verschieben die Information „Ist der Dialog offen?“ in den State. Dieses Muster heißt kontrollierte Komponente (Controlled Component).
class State(rx.State):
users: list[User] = [...]
add_user_dialog_open: bool = False # 1) Offen-Zustand im State
@rx.event
def set_add_user_dialog_open(self, open_: bool): # 2) Setter selbst definieren
self.add_user_dialog_open = open_
@rx.event
def add_user(self, form_data: dict):
self.users.append(User(**form_data))
self.add_user_dialog_open = False # 3) nach dem Absenden schließenrx.dialog.root(
...,
open=State.add_user_dialog_open, # der State bestimmt, ob der Dialog offen ist
on_open_change=State.set_add_user_dialog_open, # Öffnen/Schließen durch Nutzer wird in den State übernommen
)open= an und vergisst on_open_change, ändert sich beim Klick auf den Trigger-Button der State nicht, und der Dialog geht gar nicht erst auf. Die beiden gehören immer zusammen.on_open_change übergibt dem Handler beim Öffnen True und beim Schließen (Cancel, ESC, Klick daneben) False.State.set_variablenname ohne Definition verwendet. In 0.10 werden solche Methoden aber nicht mehr erzeugt, du musst sie also wie oben selbst definieren.open_, damit er nicht mit der eingebauten Python-Funktion open kollidiert. Das ist eine gängige Konvention.reset_on_submit=False gesetzt ist, sind die Eingabefelder beim nächsten Öffnen leer: Schließt sich der Dialog, wird sein Inhalt aus der Seite entfernt und beim erneuten Öffnen frisch gezeichnet.{"name": ..., "email": ..., "gender": ...}.add_user(form_data) aus.users, add_user_dialog_open) kommen zum Browser zurück.Im Browser prüfen — Mit allen Änderungen bis 4-4 schließt sich der Dialog nach Submit, die Tabelle bekommt eine dritte Zeile, und das Badge springt auf 3. Bleibt der Dialog offen, prüfst du noch einmal open= und on_open_change= aus 4-4.
localhost:3000
AusprobierenYour customer directory and audience breakdown.
"Other" hinzu.type="email" und probiere aus, was beim Absenden einer ungültigen Adresse passiert.return rx.toast.error("Kunde existiert bereits") zurückgeben. Gib bei Erfolg zusätzlich rx.toast.success(...) zurück.name=, form_data und den Feldern von User erklären.← Zurück
Lektion 3 · State und Vars: Daten zeichnen die Oberfläche
Weiter →
Lektion 5 · Daten aufbereiten und Diagramme
Über neue Lektionen informiert werden
Wir schicken dir eine E-Mail, wenn dieser Kurs neue Lektionen bekommt oder ein Folgekurs erscheint.
R
ReflexKR
Python und Reflex zum Anfassen. Alle Beispiele wurden mit Reflex 0.10.0 ausgeführt und geprüft.
ReflexKR ist kostenlos und ohne Werbebanner. Wenn es dir geholfen hat, kannst du es mit einem Kaffee unterstützen. ☕ Spendier uns einen Kaffee
Hinweis zu Affiliate-Links© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0
Fragen & Antworten
Frag alles zu dieser Lektion. Fragen aus allen Sprachversionen dieser Seite erscheinen hier gemeinsam.