Forum Discussion
KillGorack
Nov 09, 2024Copper Contributor
Dynamic form generation from dictionary
ASP.NET Blazor app
brand spanking new to the stuff here. Trying a move from Apache PHP.
Trying to create a dynamic form from a dictionary generated from a separate class
I've banged my head against the wall for hours and can't get past this point.
This makes everything a text box.. and When its done this way I can see the data change from the submit method.
Any time I try to create a bool (checkbox) or number field I get conversion errors or worse.
That dictionary _registry.Fields.FieldsData looks like. (converted to JSON to use here)
{
"ID": {
"ID": "47",
"fld_app": "5",
"fld_human": "ID",
"fld_column": "ID",
"fld_enable": "True",
"fld_type": "int",
"fld_pdotype": "",
"fld_length": "NULL",
"fld_precision": "",
"fld_pass": "",
"fld_opt": "False",
"fld_opt_table": "",
"fld_opt_column": "",
"fld_icon_set": "",
"fld_regex": "",
"fld_uom": "",
"fld_placeholder": "",
"fld_usr_ID": "False",
"fld_link": "",
"fld_index": "True",
"fld_detail": "True",
"fld_form": "True",
"fld_order": "1",
"fld_title": "False",
"fld_required": "False",
"fld_double": "False",
"fld_encrypt": "False",
"fld_time": "False",
"fld_image": "False",
"fld_unique": "False",
"fld_json": "False"
},
"Column1": {
"ID": "48",
"fld_app": "5",
"fld_human": "Column1",
"fld_column": "Column1",
"fld_enable": "True",
"fld_type": "nvarchar",
"fld_pdotype": "",
"fld_length": "50",
"fld_precision": "",
"fld_pass": "",
"fld_opt": "False",
"fld_opt_table": "",
"fld_opt_column": "",
"fld_icon_set": "",
"fld_regex": "",
"fld_uom": "",
"fld_placeholder": "",
"fld_usr_ID": "False",
"fld_link": "",
"fld_index": "True",
"fld_detail": "True",
"fld_form": "True",
"fld_order": "2",
"fld_title": "False",
"fld_required": "False",
"fld_double": "False",
"fld_encrypt": "False",
"fld_time": "False",
"fld_image": "False",
"fld_unique": "False",
"fld_json": "False"
},
"Column2": {
"ID": "49",
"fld_app": "5",
"fld_human": "Column2",
"fld_column": "Column2",
"fld_enable": "True",
"fld_type": "nvarchar",
"fld_pdotype": "",
"fld_length": "50",
"fld_precision": "",
"fld_pass": "",
"fld_opt": "False",
"fld_opt_table": "",
"fld_opt_column": "",
"fld_icon_set": "",
"fld_regex": "",
"fld_uom": "",
"fld_placeholder": "",
"fld_usr_ID": "False",
"fld_link": "",
"fld_index": "True",
"fld_detail": "True",
"fld_form": "True",
"fld_order": "3",
"fld_title": "False",
"fld_required": "False",
"fld_double": "False",
"fld_encrypt": "False",
"fld_time": "False",
"fld_image": "False",
"fld_unique": "False",
"fld_json": "False"
},
"Column3": {
"ID": "50",
"fld_app": "5",
"fld_human": "Column3",
"fld_column": "Column3",
"fld_enable": "True",
"fld_type": "nvarchar",
"fld_pdotype": "",
"fld_length": "50",
"fld_precision": "",
"fld_pass": "",
"fld_opt": "False",
"fld_opt_table": "",
"fld_opt_column": "",
"fld_icon_set": "",
"fld_regex": "",
"fld_uom": "",
"fld_placeholder": "",
"fld_usr_ID": "False",
"fld_link": "",
"fld_index": "True",
"fld_detail": "True",
"fld_form": "True",
"fld_order": "4",
"fld_title": "False",
"fld_required": "False",
"fld_double": "False",
"fld_encrypt": "False",
"fld_time": "False",
"fld_image": "False",
"fld_unique": "False",
"fld_json": "False"
},
"Column4": {
"ID": "51",
"fld_app": "5",
"fld_human": "Column4",
"fld_column": "Column4",
"fld_enable": "True",
"fld_type": "nvarchar",
"fld_pdotype": "",
"fld_length": "50",
"fld_precision": "",
"fld_pass": "",
"fld_opt": "False",
"fld_opt_table": "",
"fld_opt_column": "",
"fld_icon_set": "",
"fld_regex": "",
"fld_uom": "",
"fld_placeholder": "",
"fld_usr_ID": "False",
"fld_link": "",
"fld_index": "True",
"fld_detail": "True",
"fld_form": "True",
"fld_order": "5",
"fld_title": "False",
"fld_required": "False",
"fld_double": "False",
"fld_encrypt": "False",
"fld_time": "False",
"fld_image": "False",
"fld_unique": "False",
"fld_json": "False"
},
"Column5": {
"ID": "52",
"fld_app": "5",
"fld_human": "Column5",
"fld_column": "Column5",
"fld_enable": "True",
"fld_type": "nvarchar",
"fld_pdotype": "",
"fld_length": "50",
"fld_precision": "",
"fld_pass": "",
"fld_opt": "False",
"fld_opt_table": "",
"fld_opt_column": "",
"fld_icon_set": "",
"fld_regex": "",
"fld_uom": "",
"fld_placeholder": "",
"fld_usr_ID": "False",
"fld_link": "",
"fld_index": "True",
"fld_detail": "True",
"fld_form": "True",
"fld_order": "6",
"fld_title": "False",
"fld_required": "False",
"fld_double": "False",
"fld_encrypt": "False",
"fld_time": "False",
"fld_image": "False",
"fld_unique": "False",
"fld_json": "False"
},
"Column6": {
"ID": "53",
"fld_app": "5",
"fld_human": "Column6",
"fld_column": "Column6",
"fld_enable": "True",
"fld_type": "nvarchar",
"fld_pdotype": "",
"fld_length": "50",
"fld_precision": "",
"fld_pass": "",
"fld_opt": "False",
"fld_opt_table": "",
"fld_opt_column": "",
"fld_icon_set": "",
"fld_regex": "",
"fld_uom": "",
"fld_placeholder": "",
"fld_usr_ID": "False",
"fld_link": "",
"fld_index": "True",
"fld_detail": "True",
"fld_form": "True",
"fld_order": "7",
"fld_title": "False",
"fld_required": "False",
"fld_double": "False",
"fld_encrypt": "False",
"fld_time": "False",
"fld_image": "False",
"fld_unique": "False",
"fld_json": "False"
}
}
The .razor component
PAGE "/FieldsAdmin"
@inject ILogger<FieldsAdmin> Logger
@inject portalx.Classes.Main.DBO Database
@using System.Data
@using System.Collections.Generic
@inject portalx.Classes.Main._reg _registry
@using System.Text.Json
<form method="post" @onsubmit="Submit" @formname="FieldsAdmin">
<AntiforgeryToken />
@if (_registry.Fields.FieldsData != null)
{
@foreach (var row in _registry.Fields.FieldsData)
{
<div class="form-row">
@foreach (var field in row.Value)
{
<div class="form-group col-md-6">
<label>@field.Key</label>
@{
var key = $"{row.Key}-{field.Key}";
if (!Model!.DynamicFields.ContainsKey(key))
{
Model!.DynamicFields[key] = field.Value?.ToString() ?? string.Empty;
}
}
<InputText @bind-Value="Model!.DynamicFields[key]" />
</div>
}
</div>
}
}
<div>
<button type="submit">Submit</button>
</div>
</form>
<div>
<h3>Fields Data (JSON)</h3>
<pre>@jsonString</pre>
</div>
@code {
[SupplyParameterFromForm]
private ModelFieldsAdmin? Model { get; set; }
private Dictionary<string, string> dataDict { get; set; }
private string jsonString { get; set; }
protected override void OnInitialized()
{
Model ??= new();
dataDict = new Dictionary<string, string>
{
{ "ID", "hidden" },
{ "fld_app", "skip me" },
{ "fld_human", "text" },
{ "fld_column", "skip me" },
{ "fld_enable", "bool" },
{ "fld_type", "skip me" },
{ "fld_pdotype", "skip me" },
{ "fld_length", "skip me" },
{ "fld_precision", "skip me" },
{ "fld_pass", "bool" },
{ "fld_opt", "bool" },
{ "fld_opt_table", "text" },
{ "fld_opt_column", "text" },
{ "fld_icon_set", "text" },
{ "fld_regex", "text" },
{ "fld_uom", "text" },
{ "fld_placeholder", "text" },
{ "fld_usr_ID", "bool" },
{ "fld_link", "bool" },
{ "fld_index", "bool" },
{ "fld_detail", "bool" },
{ "fld_form", "bool" },
{ "fld_order", "number" },
{ "fld_title", "bool" },
{ "fld_required", "bool" },
{ "fld_double", "bool" },
{ "fld_encrypt", "bool" },
{ "fld_time", "bool" },
{ "fld_image", "bool" },
{ "fld_unique", "bool" },
{ "fld_json", "bool" }
};
jsonString = JsonSerializer.Serialize(_registry.Fields.FieldsData, new JsonSerializerOptions { WriteIndented = true });
}
private void Submit()
{
foreach (var kvp in Model!.DynamicFields)
{
Logger.LogInformation("Field Key: {Key}, Value: {Value}", kvp.Key, kvp.Value);
}
}
public class ModelFieldsAdmin
{
public string? Id { get; set; }
public Dictionary<string, string> DynamicFields { get; set; } = new Dictionary<string, string>();
}
}
Took a little time off. This version works. still need to add the table update, but this is what I was after.
PAGE "/FieldsAdmin" @inject ILogger<FieldsAdmin> Logger @inject portalx.Classes.Main.DBO Database @using System.Data @using System.Collections.Generic @inject portalx.Classes.Main._reg _registry @using System.Text.Json <form method="post" @onsubmit="Submit" @formname="FieldsAdmin"> <AntiforgeryToken /> @if (_registry.Fields.FieldsData != null) { @foreach (var row in _registry.Fields.FieldsData) { <div class="form-row"> @foreach (var field in row.Value) { <div class="form-group col-md-6"> <label>@field.Key</label> @{ var key = $"{row.Key}-{field.Key}"; if (!Model!.DynamicFields.ContainsKey(key)) { Model!.DynamicFields[key] = field.Value?.ToString() ?? string.Empty; } var fieldType = dataDict.ContainsKey(field.Key) ? dataDict[field.Key] : "text"; } @switch (fieldType) { case "number": if (!Model!.NumericFields.ContainsKey(key)) { Model!.NumericFields[key] = int.TryParse(Model!.DynamicFields[key], out var value) ? value : 0; } <InputNumber @bind-Value="Model!.NumericFields[key]" /> break; case "bool": if (!Model!.BooleanFields.ContainsKey(key)) { Model!.BooleanFields[key] = Model!.DynamicFields[key] == "True" || Model!.DynamicFields[key] == "1"; } <InputCheckbox @bind-Value="Model!.BooleanFields[key]" /> break; case "text": default: <InputText @bind-Value="Model!.DynamicFields[key]" /> break; } </div> } </div> } } <div> <button type="submit">Submit</button> </div> </form> <div> <h3>Fields Data (JSON)</h3> <pre>@jsonString</pre> </div> @code { [SupplyParameterFromForm] private ModelFieldsAdmin? Model { get; set; } private Dictionary<string, string> dataDict { get; set; } private string jsonString { get; set; } protected override void OnInitialized() { Model ??= new(); dataDict = new Dictionary<string, string> { { "ID", "hidden" }, { "fld_app", "skip me" }, { "fld_human", "text" }, { "fld_column", "skip me" }, { "fld_enable", "bool" }, { "fld_type", "skip me" }, { "fld_pdotype", "skip me" }, { "fld_length", "skip me" }, { "fld_precision", "skip me" }, { "fld_pass", "bool" }, { "fld_opt", "bool" }, { "fld_opt_table", "text" }, { "fld_opt_column", "text" }, { "fld_icon_set", "text" }, { "fld_regex", "text" }, { "fld_uom", "text" }, { "fld_placeholder", "text" }, { "fld_usr_ID", "bool" }, { "fld_link", "bool" }, { "fld_index", "bool" }, { "fld_detail", "bool" }, { "fld_form", "bool" }, { "fld_order", "number" }, { "fld_title", "bool" }, { "fld_required", "bool" }, { "fld_double", "bool" }, { "fld_encrypt", "bool" }, { "fld_time", "bool" }, { "fld_image", "bool" }, { "fld_unique", "bool" }, { "fld_json", "bool" } }; jsonString = JsonSerializer.Serialize(_registry.Fields.FieldsData, new JsonSerializerOptions { WriteIndented = true }); } private void Submit() { foreach (var kvp in Model!.DynamicFields) { Logger.LogInformation("Field Key: {Key}, Value: {Value}", kvp.Key, kvp.Value); } foreach (var kvp in Model!.NumericFields) { Logger.LogInformation("Field Key: {Key}, Value: {Value}", kvp.Key, kvp.Value); } foreach (var kvp in Model!.BooleanFields) { Logger.LogInformation("Field Key: {Key}, Value: {Value}", kvp.Key, kvp.Value); } } public class ModelFieldsAdmin { public string? Id { get; set; } public Dictionary<string, string> DynamicFields { get; set; } = new Dictionary<string, string>(); public Dictionary<string, int> NumericFields { get; set; } = new Dictionary<string, int>(); public Dictionary<string, bool> BooleanFields { get; set; } = new Dictionary<string, bool>(); } }
- KillGorackCopper Contributor
Took a little time off. This version works. still need to add the table update, but this is what I was after.
PAGE "/FieldsAdmin" @inject ILogger<FieldsAdmin> Logger @inject portalx.Classes.Main.DBO Database @using System.Data @using System.Collections.Generic @inject portalx.Classes.Main._reg _registry @using System.Text.Json <form method="post" @onsubmit="Submit" @formname="FieldsAdmin"> <AntiforgeryToken /> @if (_registry.Fields.FieldsData != null) { @foreach (var row in _registry.Fields.FieldsData) { <div class="form-row"> @foreach (var field in row.Value) { <div class="form-group col-md-6"> <label>@field.Key</label> @{ var key = $"{row.Key}-{field.Key}"; if (!Model!.DynamicFields.ContainsKey(key)) { Model!.DynamicFields[key] = field.Value?.ToString() ?? string.Empty; } var fieldType = dataDict.ContainsKey(field.Key) ? dataDict[field.Key] : "text"; } @switch (fieldType) { case "number": if (!Model!.NumericFields.ContainsKey(key)) { Model!.NumericFields[key] = int.TryParse(Model!.DynamicFields[key], out var value) ? value : 0; } <InputNumber @bind-Value="Model!.NumericFields[key]" /> break; case "bool": if (!Model!.BooleanFields.ContainsKey(key)) { Model!.BooleanFields[key] = Model!.DynamicFields[key] == "True" || Model!.DynamicFields[key] == "1"; } <InputCheckbox @bind-Value="Model!.BooleanFields[key]" /> break; case "text": default: <InputText @bind-Value="Model!.DynamicFields[key]" /> break; } </div> } </div> } } <div> <button type="submit">Submit</button> </div> </form> <div> <h3>Fields Data (JSON)</h3> <pre>@jsonString</pre> </div> @code { [SupplyParameterFromForm] private ModelFieldsAdmin? Model { get; set; } private Dictionary<string, string> dataDict { get; set; } private string jsonString { get; set; } protected override void OnInitialized() { Model ??= new(); dataDict = new Dictionary<string, string> { { "ID", "hidden" }, { "fld_app", "skip me" }, { "fld_human", "text" }, { "fld_column", "skip me" }, { "fld_enable", "bool" }, { "fld_type", "skip me" }, { "fld_pdotype", "skip me" }, { "fld_length", "skip me" }, { "fld_precision", "skip me" }, { "fld_pass", "bool" }, { "fld_opt", "bool" }, { "fld_opt_table", "text" }, { "fld_opt_column", "text" }, { "fld_icon_set", "text" }, { "fld_regex", "text" }, { "fld_uom", "text" }, { "fld_placeholder", "text" }, { "fld_usr_ID", "bool" }, { "fld_link", "bool" }, { "fld_index", "bool" }, { "fld_detail", "bool" }, { "fld_form", "bool" }, { "fld_order", "number" }, { "fld_title", "bool" }, { "fld_required", "bool" }, { "fld_double", "bool" }, { "fld_encrypt", "bool" }, { "fld_time", "bool" }, { "fld_image", "bool" }, { "fld_unique", "bool" }, { "fld_json", "bool" } }; jsonString = JsonSerializer.Serialize(_registry.Fields.FieldsData, new JsonSerializerOptions { WriteIndented = true }); } private void Submit() { foreach (var kvp in Model!.DynamicFields) { Logger.LogInformation("Field Key: {Key}, Value: {Value}", kvp.Key, kvp.Value); } foreach (var kvp in Model!.NumericFields) { Logger.LogInformation("Field Key: {Key}, Value: {Value}", kvp.Key, kvp.Value); } foreach (var kvp in Model!.BooleanFields) { Logger.LogInformation("Field Key: {Key}, Value: {Value}", kvp.Key, kvp.Value); } } public class ModelFieldsAdmin { public string? Id { get; set; } public Dictionary<string, string> DynamicFields { get; set; } = new Dictionary<string, string>(); public Dictionary<string, int> NumericFields { get; set; } = new Dictionary<string, int>(); public Dictionary<string, bool> BooleanFields { get; set; } = new Dictionary<string, bool>(); } }