Dirk Richter
Software-Entwicklung und Architektur
Formularbuilder für Blazor
Konzeption eines Formular-Builders in Blazor Dynamische Generierung von Formularfeldern:
Felder sollen basierend auf einer Konfiguration erstellt werden, ohne dass spezifischer Code geschrieben werden muss. Wiederverwendbarkeit:
Es soll möglich sein, denselben Formular-Builder in verschiedenen Anwendungen ohne Änderungen zu verwenden. Validierungen:
Unterstützen von Validierungen (z. B. erforderlich, Mindest-/Maximalwerte, reguläre Ausdrücke). Flexibles Styling:
Unterstützung von zentralen Design-Vorgaben (z. B. Themes) und Erweiterungsmöglichkeiten für spezifische Anforderungen. Konfigurationsdatei: Datenmodell: Formular-Komponente: Bindung/State-Management: Styling/Theming: Eine mögliche JSON-Repräsentation eines Formulars könnte wie folgt aussehen: Diese Konfiguration beschreibt ein Formular mit 3 Feldern: Erstelle eine Klasse, die ein Formularfeld beschreibt: Eine JSON-Konfiguration kann zur Laufzeit deserialisiert werden: Die Hauptkomponente Validierungen können basierend auf den Feld-Konfigurationen implementiert werden: Das Formular sollte einheitlich durch zentrale CSS-Klassen gestaltet werden. Für unterschiedliche Designs können Themes definiert werden: 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.
Ziele eines Formular-Builders
Architektur des Formular-Builders
Wesentliche Bestandteile
Dictionary<string, object>
).
Datenstruktur für die Konfiguration
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}
Schrittweise Umsetzung eines Formular-Builders in Blazor
1. Definieren eines Modells für Formularfelder
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
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
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
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
1.form-group {
2 margin-bottom: 16px;
3}
4
5.error-message {
6 color: red;
7 font-size: 12px;
8}
Fazit