asp.net-mvc ASP.NET MVC 中的动态(运行时生成)表单
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/926934/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Dynamic (Runtime Generated) Forms in ASP.NET MVC
提问by Robert Claypool
This is a general design question: How would you implement a dynamic (runtime generated) form in ASP.NET MVC?
这是一个通用的设计问题: 您将如何在 ASP.NET MVC 中实现动态(运行时生成)表单?
Here's the situation:
这是情况:
- A site admin can define form parameters (fields, type of fields, validation) with a GUI (MVC view).
- As needed, the runtime generates the form for the end user based on the admin configuration. I'm assuming that all of this logic would reside in the controller - or perhaps extension methods, action filters or something like that.
- End user fills out the form, hits submit, information is captured in database.
- 站点管理员可以使用 GUI(MVC 视图)定义表单参数(字段、字段类型、验证)。
- 根据需要,运行时会根据管理员配置为最终用户生成表单。我假设所有这些逻辑都将驻留在控制器中——或者可能是扩展方法、动作过滤器或类似的东西。
- 最终用户填写表格,点击提交,信息被捕获到数据库中。
The customization does not need to support nested controls, 3rd party controls and so forth, but I suspect a very elegant design would allow for that. Mostly, I just need the admin to be able to specify additional fields as textboxes, checkboxes, radio buttons and comboboxes. I will also need the application to allocate a space for this data to be saved in the db, but I believe I have that part figured out.
自定义不需要支持嵌套控件、第 3 方控件等,但我怀疑一个非常优雅的设计将允许这样做。大多数情况下,我只需要管理员能够将其他字段指定为文本框、复选框、单选按钮和组合框。我还需要应用程序为要保存在数据库中的数据分配一个空间,但我相信我已经弄清楚了那部分。
Thanks for the help.
谢谢您的帮助。
回答by Ronnie Overby
I had the same need in a recent project. I created a class library for this. I just released a new version of the library.
我在最近的一个项目中也有同样的需求。我为此创建了一个类库。我刚刚发布了一个新版本的库。
Maybe it can help you: ASP.NET MVC Dynamic Forms
也许它可以帮助您: ASP.NET MVC 动态表单
回答by mcintyre321
You can do this very easily using my FormFactorylibrary.
您可以使用我的FormFactory库轻松完成此操作。
By default it reflects against a view model to produce a PropertyVm[]array, but you can also create the properties programatically, so you could load settings from a database then create PropertyVm.
默认情况下,它会根据视图模型进行反射以生成PropertyVm[]数组,但您也可以以编程方式创建属性,因此您可以从数据库加载设置,然后创建PropertyVm.
This is a snippet from a Linqpad script.
这是来自 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 sitewith examples of the various features you can set up (e.g. nested collections, autocomplete, datepickers etc.)
有一个演示站点,其中包含您可以设置的各种功能的示例(例如嵌套集合、自动完成、日期选择器等)
回答by David
Another option is to have a very loosely coupled database schema.
另一种选择是拥有一个非常松散耦合的数据库模式。
//this will contain all the fields and types that the admin user sets **ApplicationFields** FieldName FieldType ... //these are all the values that have some mapping to a ParentObjectID **FormValues** ParentObjectID FieldName FieldValue
When you submit your runtime generated View (from ApplicationFields) then just loop through your FormCollection and try and set it on the ParentObject you need to update.
当您提交运行时生成的视图(来自 ApplicationFields)时,只需循环遍历您的 FormCollection 并尝试将其设置在您需要更新的 ParentObject 上。
public ActionResult MyForm(FormCollection form)
{
//this is the main object that contains all the fields
var parentObject;
foreach (string key in form)
{
parentObject.SetValue(key, form[key]);
}
...
Then your parentObject might be something like this...
那么你的 parentObject 可能是这样的......
public partial class ParentObject
{
IList _FormValues;
public void SetValue(string key, string value)
{
//try and find if this value already exists
FormValue v = _FormValues.SingleOrDefault(k => k.Key == key);
//if it does just set it
if (v != null)
{
v.Value = value;
return;
}
//else this might be a new form field added and therefore create a new value
v = new FormValue
{
ParentObjectID = this.ID,
Key = key,
Value = value
};
_FormValues.Add(v);
}
}
回答by Martijn Laarman
One way to do this is to create your own ModelBinderwhich would be at the heart of your generated forms. A modelbinder is responsible for validating the ModelStateand rebuilding the typed ViewDataModel(assuming your views are typed).
一种方法是创建您自己的ModelBinder,这将是您生成的表单的核心。模型绑定器负责验证ModelState和重建类型ViewDataModel(假设您的视图已输入)。
The DataAnnotationsmodel binder could be a good reference for this what this custom modelbinder allows you to do is via Attributeson your ViewDataModeldescribe the attribute's validation (and hint at UI rendering). However this is all defined compile time but would be a great reference to start off writing a custom modelbinder.
该DataAnnotations模型绑定可能是一个很好的参考是通过此自定义模型绑定器可以让你做Attributes你的ViewDataModel描述属性的验证(并暗示在UI渲染)。然而,这是所有定义的编译时间,但对于开始编写自定义模型绑定器将是一个很好的参考。
In your case your model binder should get the validation for a field at runtime from an xml file/string.
在您的情况下,您的模型绑定器应该在运行时从 xml 文件/字符串中获取对字段的验证。
If you have a route like:
如果你有这样的路线:
routes.MapRoute(null, "Forms/{formName}/", new { action = "Index", controller = "Forms", formName = ""}),
Then you could locate the correct form xml in FormsController.Index(string formName)and pass it to the view.
然后您可以在其中找到正确的表单 xmlFormsController.Index(string formName)并将其传递给视图。
The FormsModelshould hold all the possible methods to get data I dont see any other way around this. The Xml could map to a function name (possibly even arguments) that you can invoke using reflection on the FormsModelto fill the ViewDataor typed ViewDataModelwith data.
本FormsModel应持有的所有可能的方法来获取数据,我没有看到解决这个任何其他方式。Xml 可以映射到一个函数名(甚至可能是参数),您可以使用反射来调用它FormsModel来填充ViewData或键入ViewDataModel数据。
The view for Form Index could generate a form from that xml through an HtmlHelperExtension that takes an XmlDocument.
表单索引的视图可以通过HtmlHelper扩展名从该 xml 生成表单,该扩展名为XmlDocument.
Then when you (or asp.net mvc) binds your form to your ViewDatayour custom model binder is invoked it can inspect the current controller values to look for the formName and look up the corresponding xml that holds all the validation rules. The ModelBinderis then responsible for filling up ModelStatewith any runtime defined errors.
然后,当您(或 asp.net mvc)将您的表单绑定到您ViewData的自定义模型绑定器时,它可以检查当前控制器值以查找 formName 并查找包含所有验证规则的相应 xml。该ModelBinder是则填补了负责ModelState与任何运行时定义的错误。
It's a hard task but when pulled off succesfully well worth it in my view :)
这是一项艰巨的任务,但在我看来,成功完成后非常值得:)
Updatea better alternative to model data would be a very loose database schema as David Liddle suggests. I'd still go through the trouble of saving it as xml (or someother serialized format) and using that for generating the view and to hold validation rules for a custom ModelBinderso that you have more control on layout and validation of each field.
更新模型数据的更好替代方案是非常松散的数据库模式,正如 David Liddle 所建议的那样。我仍然会遇到将其保存为 xml(或其他序列化格式)并使用它来生成视图并保存自定义验证规则的麻烦,ModelBinder以便您可以更好地控制每个字段的布局和验证。
回答by Martijn Laarman
cottsak's answer is very attractive.
cottsak 的回答很吸引人。
There are at least two client-side XForms engines. Here's one:
至少有两个客户端 XForms 引擎。这是一个:
回答by Roman Pokrovskij
I can't see huge advantages of generating XForms or any other "abstraction" over the HTML comparing with straight forward generation of HTML with "Web Forms 2.0" controls list for model like List<Tuple<Meta, Value>>. Note: on server side you in any case would be obligated to parse results manually to fit it to your structrures.
与使用“Web Forms 2.0”控件列表直接生成 HTML 相比,我看不到生成 XForms 或任何其他“抽象”比 HTML 的巨大优势List<Tuple<Meta, Value>>。注意:在服务器端,您无论如何都必须手动解析结果以使其适合您的结构。
Searching for "next layer abstractions" is good for rapid development, but see, "generate code" (runtime or build-time) has its own specific. Usually the generating code of "lower layer" is the better solution than generating the "higher abstract layer" code.
搜索“下一层抽象”有利于快速开发,但请注意,“生成代码”(运行时或构建时)有其特定的含义。通常“较低层”的生成代码比生成“较高抽象层”的代码更好。
So just go and wirte code that generate Web 2 Controls in @Foreach loop.
因此,只需编写在@Foreach 循环中生成 Web 2 控件的代码即可。

