我要求为不同的客户端提供不同的表单,这些表单都可以在后台配置(最后在数据库中)
我最初的想法是为“Form”创建一个对象,它有一个“FormItem字典”来描述表单字段。
然后,我可以通过执行以下操作来创建动态表单(这将来自数据库/服务):
private Form GetFormData()
{
var dict = new Dictionary<string, FormItem>();
dict.Add("FirstName", new FormItem()
{
FieldType = Core.Web.FieldType.TextBox,
FieldName = "FirstName",
Label = "FieldFirstNameLabel",
Value = "FName"
});
dict.Add("LastName", new FormItem()
{
FieldType = Core.Web.FieldType.TextBox,
FieldName = "LastName",
Label = "FieldLastNameLabel",
Value = "LName"
});
dict.Add("Submit", new FormItem()
{
FieldType = Core.Web.FieldType.Submit,
FieldName = "Submit",
Label = null,
Value = "Submit"
});
var form = new Form()
{
Method = "Post",
Action = "Index",
FormItems = dict
};
return form;
}
在我的控制器中,我可以获取表单数据并将其传递到视图
public IActionResult Index()
{
var formSetup = GetFormData(); // This will call into the service and get the form and the values
return View(formSetup);
}
在视图中,我为每个FormItems
调用了一个HtmlHelper@model Form
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@using FormsSpike.Core.Web
@{
ViewData["Title"] = "Home Page";
}
@using (Html.BeginForm(Model.Action, "Home", FormMethod.Post))
{
foreach (var item in Model.FormItems)
{
@Html.FieldFor(item);
}
}
然后在回发时我必须遍历表单变量并再次匹配它们。这种感觉非常古老,我希望可以在某种模型装订器中完成。
[HttpPost]
public IActionResult Index(IFormCollection form)
{
var formSetup = GetFormData();
foreach (var formitem in form)
{
var submittedformItem = formitem;
if (formSetup.FormItems.Any(w => w.Key == submittedformItem.Key))
{
FormItem formItemTemp = formSetup.FormItems.Single(w => w.Key == submittedformItem.Key).Value;
formItemTemp.Value = submittedformItem.Value;
}
}
return View("Index", formSetup);
}
然后,我可以运行一些映射,它将在后台更新数据库。
我的问题是这感觉不对:o {
此外,我使用了一个非常简单的HtmlHelper但是我无法真正使用标准的htmlHelpers(例如LabelFor)来创建表单,因为没有要绑定的模型..
public static HtmlString FieldFor(this IHtmlHelper html, KeyValuePair<string, FormItem> item)
{
string stringformat = "";
switch (item.Value.FieldType)
{
case FieldType.TextBox:
stringformat = $"<div class='formItem'><label for='item.Key'>{item.Value.Label}</label><input type='text' id='{item.Key}' name='{item.Key}' value='{item.Value.Value}' /></ div >";
break;
case FieldType.Number:
stringformat = $"<div class='formItem'><label for='item.Key'>{item.Value.Label}</label><input type='number' id='{item.Key}' name='{item.Key}' value='{item.Value.Value}' /></ div >";
break;
case FieldType.Submit:
stringformat = $"<input type='submit' name='{item.Key}' value='{item.Value.Value}'>";
break;
default:
break;
}
return new HtmlString(stringformat);
}
此外,验证将不起作用,因为属性(例如RegExAttribute的RequiredAttribute)不存在。
我对此采取了错误的方法,还是有更明确的方式来完成这样的表单?
有没有办法创建一个动态的ViewModel,它可以从原始设置创建并仍然保留所有MVC丰富度?
答案 0 :(得分:1)
您可以使用我的FormFactory库执行此操作。
默认情况下,它会反映视图模型以生成PropertyVm[]
数组:
```
var vm = new MyFormViewModel
{
OperatingSystem = "IOS",
OperatingSystem_choices = new[]{"IOS", "Android",};
};
Html.PropertiesFor(vm).Render(Html);
```
但您也可以以编程方式创建属性,因此您可以从数据库加载设置,然后创建PropertyVm
。
这是Linqpad脚本的一个片段。
```
//import-package FormFactory
//import-package FormFactory.RazorGenerator
void Main()
{
var properties = new[]{
new PropertyVm(typeof(string), "username"){
DisplayName = "Username",
NotOptional = true,
},
new PropertyVm(typeof(string), "password"){
DisplayName = "Password",
NotOptional = true,
GetCustomAttributes = () => new object[]{ new DataTypeAttribute(DataType.Password) }
}
};
var html = FormFactory.RazorEngine.PropertyRenderExtension.Render(properties, new FormFactory.RazorEngine.RazorTemplateHtmlHelper());
Util.RawHtml(html.ToEncodedString()).Dump(); //Renders html for a username and password field.
}
```
Theres a demo site,其中包含您可以设置的各种功能的示例(例如嵌套集合,自动填充,日期选择等)。
答案 1 :(得分:1)
我将把我的解决方案放在这里,因为我发现了这个搜索“如何在 mvc 核心中创建动态表单”。我不想使用第 3 方库。
型号:
public class IndexViewModel
{
public Dictionary<int, DetailTemplateItem> FormBody { get; set; }
public string EmailAddress { get; set; }
public string templateName { get; set; }
}
cshtml
<form asp-action="ProcessResultsDetails" asp-controller="home" method="post">
<div class="form-group">
<label asp-for=@Model.EmailAddress class="control-label"></label>
<input asp-for=@Model.EmailAddress class="form-control" />
</div>
@foreach (var key in Model.FormBody.Keys)
{
<div class="form-group">
<label asp-for="@Model.FormBody[key].Name" class="control-label">@Model.FormBody[key].Name</label>
<input asp-for="@Model.FormBody[key].Value" class="form-control" value="@Model.FormBody[key].Value"/>
<input type="hidden" asp-for="@Model.FormBody[key].Name"/>
</div>
}
<input type="hidden" asp-for="templateName" />
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>