Dirk Richter
Software-Entwicklung und Architektur

Formularbuilder für Blazor


Konzeption eines Formular-Builders in Blazor

Ziele eines Formular-Builders

  1. Dynamische Generierung von Formularfeldern: Felder sollen basierend auf einer Konfiguration erstellt werden, ohne dass spezifischer Code geschrieben werden muss.

  2. Wiederverwendbarkeit: Es soll möglich sein, denselben Formular-Builder in verschiedenen Anwendungen ohne Änderungen zu verwenden.

  3. Validierungen: Unterstützen von Validierungen (z. B. erforderlich, Mindest-/Maximalwerte, reguläre Ausdrücke).

  4. Flexibles Styling: Unterstützung von zentralen Design-Vorgaben (z. B. Themes) und Erweiterungsmöglichkeiten für spezifische Anforderungen.

Architektur des Formular-Builders

Wesentliche Bestandteile

  1. Konfigurationsdatei:

    • Die Struktur des Formulars sollte durch eine Konfigurationsdatei definiert werden. Dies könnte ein JSON-, XML-Format oder ein programmgesteuertes Modell in C# sein.
  2. Datenmodell:

    • Ein dynamisches Objekt, das die gebundenen Werte speichert (z. B. Dictionary<string, object>).
  3. Formular-Komponente:

    • Die Hauptkomponente, die:
      • Das Formular rendert,
      • Eingabefelder basierend auf der Konfiguration generiert,
      • Validierungen unterstützt.
  4. Bindung/State-Management:

    • Ein Mechanismus zum Aktualisieren und Teilen von Formulareinträgen in der Anwendung, z. B. mit einem zentralen State oder Dependency Injection.
  5. Styling/Theming:

    • Eine flexible Methode, um das Aussehen der Eingabekomponenten anzupassen.

Datenstruktur für die Konfiguration

Eine mögliche JSON-Repräsentation eines Formulars könnte wie folgt aussehen:

 1{
 2  "formName": "UserForm",
 3  "fields": [
 4    {
 5      "name": "firstName",
 6      "label": "Vorname",
 7      "type": "text",
 8      "required": true,
 9      "maxLength": 50
10    },
11    {
12      "name": "age",
13      "label": "Alter",
14      "type": "number",
15      "required": true,
16      "min": 18,
17      "max": 100
18    },
19    {
20      "name": "gender",
21      "label": "Geschlecht",
22      "type": "select",
23      "options": ["Männlich", "Weiblich", "Andere"]
24    }
25  ]
26}

Diese Konfiguration beschreibt ein Formular mit 3 Feldern:

Schrittweise Umsetzung eines Formular-Builders in Blazor

1. Definieren eines Modells für Formularfelder

Erstelle eine Klasse, die ein Formularfeld beschreibt:

 1public class FormFieldConfig
 2{
 3    public string Name { get; set; }          // Name des Feldes
 4    public string Label { get; set; }         // Beschriftung
 5    public string Type { get; set; }          // Feldtyp: text, number, select, etc.
 6    public bool Required { get; set; }        // Ist dieses Feld erforderlich?
 7    public int? MaxLength { get; set; }       // Maximale Länge für Textfelder
 8    public int? Min { get; set; }             // Minimalwert (für Zahlen)
 9    public int? Max { get; set; }             // Maximalwert (für Zahlen)
10    public IEnumerable<string> Options { get; set; } // Optionen für Dropdowns
11}

2. Laden der Formular-Konfiguration

Eine JSON-Konfiguration kann zur Laufzeit deserialisiert werden:

 1public class FormConfig
 2{
 3    public string FormName { get; set; }
 4    public List<FormFieldConfig> Fields { get; set; }
 5}
 6
 7// Beispiel für das Laden aus JSON
 8private async Task<FormConfig> LoadFormConfigFromJsonAsync(string jsonPath)
 9{
10    using var stream = File.OpenRead(jsonPath);
11    var formConfig = await JsonSerializer.DeserializeAsync<FormConfig>(stream);
12    return formConfig;
13}

3. Dynamische Generierung der UI-Komponenten

Die Hauptkomponente DynamicForm erhält die Feld-Konfiguration und rendert die passenden Eingabekomponenten:

<EditForm Model="@boundValues" OnInvalidSubmit="@HandleInvalidSubmit" OnValidSubmit="@HandleValidSubmit">
    @foreach (var field in FormConfig.Fields)
    {
        <div class="form-group">
            <label for="@field.Name">@field.Label</label>
            
            @if (field.Type == "text")
            {
                <InputText id="@field.Name" @bind-Value="boundValues[field.Name]" />
            }
            else if (field.Type == "number")
            {
                <InputNumber id="@field.Name" @bind-Value="boundValues[field.Name]" Min="@field.Min" Max="@field.Max" />
            }
            else if (field.Type == "select")
            {
                <InputSelect id="@field.Name" @bind-Value="boundValues[field.Name]">
                    @foreach (var option in field.Options)
                    {
                        <option value="@option">@option</option>
                    }
                </InputSelect>
            }

            <!-- Fehleranzeige -->
            @if (validationMessages.ContainsKey(field.Name))
            {
                <span class="error-message">@validationMessages[field.Name]</span>
            }
        </div>
    }
    <button type="submit">Senden</button>
</EditForm>

@code {
    [Parameter] public FormConfig FormConfig { get; set; }
    private Dictionary<string, object> boundValues = new();
    private Dictionary<string, string> validationMessages = new();

    private Task HandleInvalidSubmit()
    {
        Console.WriteLine("Formular hat Validierungsfehler.");
        return Task.CompletedTask;
    }

    private Task HandleValidSubmit()
    {
        Console.WriteLine("Formular erfolgreich gesendet!");
        Console.WriteLine(boundValues);
        return Task.CompletedTask;
    }
}

4. Validierungen hinzufügen

Validierungen können basierend auf den Feld-Konfigurationen implementiert werden:

 1private void ValidateForm()
 2{
 3    validationMessages.Clear();
 4
 5    foreach (var field in FormConfig.Fields)
 6    {
 7        if (field.Required && (boundValues[field.Name] == null || string.IsNullOrWhiteSpace(boundValues[field.Name]?.ToString())))
 8        {
 9            validationMessages[field.Name] = $"{field.Label} ist erforderlich.";
10        }
11
12        if (field.MaxLength.HasValue && boundValues[field.Name]?.ToString()?.Length > field.MaxLength)
13        {
14            validationMessages[field.Name] = $"{field.Label} darf maximal {field.MaxLength} Zeichen lang sein.";
15        }
16
17        if (field.Type == "number" && field.Min.HasValue && field.Max.HasValue)
18        {
19            if (int.TryParse(boundValues[field.Name]?.ToString(), out int value))
20            {
21                if (value < field.Min || value > field.Max)
22                {
23                    validationMessages[field.Name] = $"{field.Label} muss zwischen {field.Min} und {field.Max} liegen.";
24                }
25            }
26        }
27    }
28}

5. Styling und Themes

Das Formular sollte einheitlich durch zentrale CSS-Klassen gestaltet werden. Für unterschiedliche Designs können Themes definiert werden:

1.form-group {
2    margin-bottom: 16px;
3}
4
5.error-message {
6    color: red;
7    font-size: 12px;
8}

Fazit

Ein Formular-Builder in Blazor bietet eine flexible und wiederverwendbare Lösung zur Erstellung dynamischer Formulare. Durch eine Konfigurationsdatei, ein flexibles Modell und Wiederverwendbarkeit lassen sich Wizards oder andere Eingabemasken effizient umsetzen. Neben der Implementierungsgrundlage können erweiterte Funktionen wie Übersetzung, Mehrsprachigkeit und Designwechsel eingebaut werden.

#Blazor #Design #Development