Benutzerdefinierte Felder
Benutzerdefinierte Felder ermöglichen es Ihnen, vollständig zu steuern, wie ein Eigenschaftswert in einem Formular bearbeitet und angezeigt wird. Anstelle des integrierten Renderers für einen dataType stellen Sie eine React-Komponente bereit. Diese Komponente erhält einen umfangreichen Satz von Props (FieldProps), damit sie:
- Den aktuellen Wert lesen und aktualisieren kann (
value,setValue) - Jede andere Eigenschaft im selben Formular aktualisieren kann (
setFieldValueodercontext.setFieldValue) - Auf alle aktuellen Entity-Werte + Formular-Utilities zugreifen kann (
context) - Den Formularzustand respektieren kann (
isSubmitting,disabled,showError,error,touched) - Das Layout anpassen kann (
size,partOfArray,minimalistView,autoFocus) - Entwicklerdefinierte
customPropsverwenden kann
Wann sollten Sie ein benutzerdefiniertes Feld erstellen?
Abschnitt betitelt „Wann sollten Sie ein benutzerdefiniertes Feld erstellen?“Verwenden Sie ein benutzerdefiniertes Feld, wenn Sie eines (oder mehrere) der Folgenden benötigen:
- Einen visuellen Stil, der von integrierten Komponenten nicht abgedeckt wird (Farbwähler, Tag-Eingaben, Schieberegler, Diagramme, KI-gestützte Felder, etc.)
- Zusammengesetzte UI, die mehrere Eigenschaften kombiniert (z. B. Lat/Lng-Kartenauswahl, die in zwei numerische Felder schreibt)
- Integrationen (Upload zu einer externen API, Abrufen von Vorschlägen, Geocoding, etc.)
Wenn Sie nur Validierung oder einfache Transformation benötigen, bevorzugen Sie zunächst Validierungsoptionen auf Eigenschaftsebene, um die Dinge einfach zu halten. Wenn Sie dynamisches Verhalten abhängig von anderen Werten benötigen, ziehen Sie stattdessen bedingte Felder in Betracht.
Beispiel für ein benutzerdefiniertes Feld
Abschnitt betitelt „Beispiel für ein benutzerdefiniertes Feld“Ein benutzerdefiniertes Textfeld mit einer über customProps bereitgestellten Hintergrundfarbe (scrollen Sie nach unten für den vollständigen Prop-Vertrag und fortgeschrittene Techniken):
import React from "react";import { FieldHelperText, FieldProps, useModeController } from "@firecms/core";import { TextField } from "@firecms/ui";
interface CustomColorTextFieldProps { color: string;}
export default function CustomColorTextField({ property, value, setValue, customProps, includeDescription, showError, error, isSubmitting, context}: FieldProps<string, CustomColorTextFieldProps>) {
const { mode } = useModeController(); const backgroundColor = customProps?.color ?? (mode === "light" ? "#eef4ff" : "#16325f");
return ( <> <TextField inputStyle={{ backgroundColor }} error={!!error} disabled={isSubmitting} label={error ?? property.name} value={value ?? ""} onChange={(evt: any) => setValue(evt.target.value)} /> <FieldHelperText includeDescription={includeDescription} showError={showError} error={error} property={property} /> </> );}Verwendung in einer Kollektion:
export const blogCollection = buildCollection({ id: "blog", path: "blog", name: "Blog entry", properties: { // ... andere Eigenschaften gold_text: { name: "Gold text", description: "Dieses Feld verwendet eine benutzerdefinierte Komponente, die vom Entwickler definiert wurde", dataType: "string", Field: CustomColorTextField, customProps: { color: "gold" } } }});Komponentenvertrag (FieldProps)
Abschnitt betitelt „Komponentenvertrag (FieldProps)“Ihre Komponente muss mindestens:
- Den aktuellen
valuelesen (er kannundefinedodernullfür leer sein) setValue(newValue)aufrufen, wenn der Benutzer ihn ändert
Empfohlene gute Praktiken:
disabled/isSubmittingrespektieren- Das Label und den Fehler anzeigen (verwenden Sie Ihre eigene UI oder
<FieldHelperText>/ integrierte Komponenten) - Schwere Seiteneffekte bei jedem Tastendruck vermeiden (Netzwerkaufrufe entprellen)
Vollständige Schnittstelle: FieldProps (enthält detaillierte Kommentare).
Benutzerdefinierte Props übergeben
Abschnitt betitelt „Benutzerdefinierte Props übergeben“Geben Sie ein customProps-Objekt in der Eigenschaftsdefinition an. Das Objekt ist stark typisiert über den zweiten Generic von FieldProps<T, CustomProps>.
Auf den Rest der Entity zugreifen (Formularkontext)
Abschnitt betitelt „Auf den Rest der Entity zugreifen (Formularkontext)“context gibt Ihnen Live-Zugriff auf:
- Alle aktuellen Werte (
context.values) context.setFieldValue(key, value)zum Aktualisieren eines anderen Feldscontext.save(values)zum programmatischen Auslösen eines Speichvorgangs (selten in Feldern benötigt)- Metadaten:
entityId,status(neu/vorhanden/Kopie),collection,openEntityMode,disabled
Dies ermöglicht feldübergreifende Logik (z. B. automatisch Slug erzeugen, wenn sich der Titel ändert) oder bedingtes Deaktivieren.
Andere Eigenschaften in einem benutzerdefinierten Feld rendern (oder zusammensetzen)
Abschnitt betitelt „Andere Eigenschaften in einem benutzerdefinierten Feld rendern (oder zusammensetzen)“Wenn Ihr benutzerdefiniertes Feld die UI einer anderen Eigenschaft einbeziehen möchte, verwenden Sie PropertyFieldBinding.
Dies erhält Validierung und Konsistenz:
import { PropertyFieldBinding } from "@firecms/core";
<PropertyFieldBinding propertyKey="subtitle" property={collection.properties.subtitle} context={context} includeDescription/>Dies ist ideal für zusammengesetzte Widgets, die mehrere zugrunde liegende Werte orchestrieren.
Zum Beispiel ist das integrierte Standard-Widget für map nur ein Wrapper um die definierten Eigenschaften
Arrays & verschachtelte Daten handhaben
Abschnitt betitelt „Arrays & verschachtelte Daten handhaben“Wenn sich Ihr benutzerdefiniertes Feld innerhalb eines Arrays befindet:
partOfArrayisttrue- Sie können einen Index in einem übergeordneten Kontext erhalten, wenn Sie verschachtelte Array-Editoren erstellen
Für verschachtelte Werte (z. B. address.street in einem zusammengesetzten Feld bearbeiten) rufen Sie setFieldValue("address.street", value) auf.
Validierungsstrategien
Abschnitt betitelt „Validierungsstrategien“Bevorzugen Sie deklarative Validierung in der Eigenschaftskonfiguration, wenn möglich. Sie können dennoch clientseitige Guards im Feld implementieren (z. B. ungültige Tastendrücke ignorieren), aber erlauben Sie der zentralen Validierung, Fehler anzuzeigen.
Häufige Muster:
- Beim Verlassen trimmen, aber Benutzereingaben beibehalten: Roh-Eingabe im lokalen Zustand halten,
setValuemit bereinigtem Wert beim Verlassen aufrufen. - Asynchrone Validierung (z. B. Eindeutigkeit): Prüfung entprellen, einen vorübergehenden lokalen Fehler setzen, Tippen nicht blockieren.
Performance-Tipps
Abschnitt betitelt „Performance-Tipps“- Netzwerk- oder aufwändige Berechnungen entprellen (
useEffect+setTimeoutoder ein Utility) anstatt bei jedem Tastendruck. - Schwere Kindkomponenten basierend auf relevanten Props memoizen.
- Vermeiden Sie das Speichern großer abgeleiteter Objekte im Zustand; leiten Sie sie beim Rendern ab oder memoizen Sie.
Erweitertes Beispiel: Zusammengesetzter Slug-Editor
Abschnitt betitelt „Erweitertes Beispiel: Zusammengesetzter Slug-Editor“Generiert automatisch einen Slug aus dem Titel, erlaubt aber manuelle Überschreibung.
function SlugField({ value, setValue, context, property, showError, error }: FieldProps<string>) { const title = context.values.title as string | undefined;
React.useEffect(() => { if (!value && title) { const auto = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, ""); setValue(auto); } }, [title]);
return ( <TextField label={property.name} value={value ?? ""} error={!!error} onChange={(e: any) => setValue(e.target.value)} helperText={showError ? error : "Wird automatisch aus dem Titel generiert, wenn leer"} /> );}PropertyFieldBinding in einem zusammengesetzten Feld verwenden
Abschnitt betitelt „PropertyFieldBinding in einem zusammengesetzten Feld verwenden“function GeoPointField({ context }: FieldProps<any>) { return ( <div style={{ display: "flex", gap: 8 }}> <PropertyFieldBinding propertyKey="lat" property={context.collection?.properties.lat} context={context} minimalistView /> <PropertyFieldBinding propertyKey="lng" property={context.collection?.properties.lng} context={context} minimalistView /> {/* Könnte eine Kartenauswahl hinzufügen, die context.setFieldValue("lat", newLat) aufruft */} </div> );}Fehlerbehebung & Fallstricke
Abschnitt betitelt „Fehlerbehebung & Fallstricke“- Wert wird nicht aktualisiert: Stellen Sie sicher, dass Sie
setValueaufrufen (nichtvaluedirekt mutieren) und dass Sie denvaluenicht im lokalen Zustand überschatten, ohne zu synchronisieren. - Fehler wird nie angezeigt: Denken Sie daran, dass
showErrordie visuelle Anzeige steuert;errorkann existieren, währendshowErrorfalse ist. - Feldübergreifende Updates werden ignoriert: Verwenden Sie den genauen Eigenschaftsschlüssel (z. B.
address.street, Array-Indizes wieitems[0].price). - Feld wird zu oft neu gerendert: Schließen Sie schwere Logik in
useMemo/useCallbackein, vermeiden Sie die Erstellung neuer Objekte bei jedem Render. - Nur-Lese-Modus benötigt: Respektieren Sie
disabledaus Props odercontext.disabled.
Nächste Schritte
Abschnitt betitelt „Nächste Schritte“- Weitere Anpassungsmöglichkeiten erkunden: Benutzerdefinierte Vorschauen
- Logik über viele Eigenschaften hinweg wiederverwenden: eine gemeinsame Feldkomponente erstellen und verschiedene
customPropsübergeben. - Open-Source-freundlich? Erwägen Sie, ein wiederverwendbares Feld zur Community beizutragen.
Durch die Nutzung benutzerdefinierter Felder können Sie reichhaltige Bearbeitungserlebnisse schaffen, die eng mit der Domain Ihres Produkts abgestimmt sind, während Validierung, Zustand und Persistenz in FireCMS zentralisiert bleiben.