24

这是一个通用的设计问题: 您将如何在 ASP.NET MVC 中实现动态(运行时生成)表单?

情况如下:

  1. 站点管理员可以使用 GUI(MVC 视图)定义表单参数(字段、字段类型、验证)。
  2. 根据需要,运行时会根据管理员配置为最终用户生成表单。我假设所有这些逻辑都将驻留在控制器中——或者可能是扩展方法、动作过滤器或类似的东西。
  3. 最终用户填写表格,点击提交,信息被捕获到数据库中。

自定义不需要支持嵌套控件、第 3 方控件等,但我怀疑一个非常优雅的设计会允许这样做。大多数情况下,我只需要管理员能够将其他字段指定为文本框、复选框、单选按钮和组合框。我还需要应用程序为要保存在数据库中的这些数据分配空间,但我相信我已经弄清楚了这部分。

谢谢您的帮助。

4

6 回答 6

13

我在最近的一个项目中也有同样的需求。我为此创建了一个类库。我刚刚发布了一个新版本的库。

也许它可以帮助你: ASP.NET MVC 动态表单

于 2010-02-01T00:33:56.150 回答
4

您可以使用我的FormFactory库非常轻松地做到这一点。

默认情况下,它会根据视图模型反映以生成PropertyVm[]数组,但您也可以通过编程方式创建属性,因此您可以从数据库加载设置然后创建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.
}

```

有一个演示站点,其中包含您可以设置的各种功能的示例(例如嵌套集合、自动完成、日期选择器等)

于 2016-07-11T14:18:55.003 回答
3

另一种选择是拥有一个非常松散耦合的数据库模式。

//这将包含管理员用户设置的所有字段和类型
**应用领域**
字段名
字段类型
...

//这些都是映射到 ParentObjectID 的所有值
**表单值**
父对象 ID
字段名
字段值

当您提交运行时生成的视图(来自 ApplicationFields)时,只需遍历您的 FormCollection 并尝试将其设置在您需要更新的 ParentObject 上。

public ActionResult MyForm(FormCollection 表单)
{
    //这是包含所有字段的主要对象
    变种父对象;

    foreach(表单中的字符串键)
    {
        parentObject.SetValue(key, form[key]);
    }
    ...

那么你的 parentObject 可能是这样的......

公共部分类 ParentObject
{
    IList _FormValues;

    public void SetValue(字符串键,字符串值)
    {
        //尝试查找该值是否已经存在
        FormValue v = _FormValues.SingleOrDefault(k => k.Key == key);

        //如果它只是设置它
        如果 (v != null)
        {
            v.价值=价值;
            返回;
        }

        //否则这可能是添加的新表单字段,因此会创建一个新值
        v = 新的表单值
        {
            ParentObjectID = this.ID,
            钥匙=钥匙,
            价值=价值
        };

        _FormValues.Add(v);
    }
}
于 2009-05-29T17:30:46.823 回答
1

一种方法是创建您自己的ModelBinder,这将是您生成的表单的核心。模型绑定器负责验证ModelState和重建类型ViewDataModel(假设您的视图是类型化的)。

DataAnnotations模型绑定器可能是一个很好的参考,此自定义模型绑定器允许您通过Attributes描述ViewDataModel属性的验证(并提示 UI 呈现)来执行此操作。然而,这都是定义的编译时间,但对于开始编写自定义模型绑定器将是一个很好的参考。

在您的情况下,您的模型绑定器应该在运行时从 xml 文件/字符串中获取字段的验证。

如果你有这样的路线:

routes.MapRoute(null, "Forms/{formName}/", new { action = "Index", controller = "Forms", formName = ""}),

然后您可以在其中找到正确的表单 xmlFormsController.Index(string formName)并将其传递给视图。

应该包含所有可能的FormsModel方法来获取数据,我看不到任何其他方式。Xml 可以映射到一个函数名(甚至可能是参数),您可以使用反射调用该函数名FormsModel来填充ViewData或键入ViewDataModel数据。

表单索引的视图可以通过一个HtmlHelper扩展从该 xml 生成一个表单,该扩展采用XmlDocument.

然后,当您(或 asp.net mvc)将您的表单绑定到您ViewData的自定义模型绑定器时,它可以检查当前控制器值以查找 formName 并查找包含所有验证规则的相应 xml。然后ModelBinder负责填充ModelState任何运行时定义的错误。

这是一项艰巨的任务,但在我看来,成功完成后非常值得:)

正如 David Liddle 所建议的那样,更新模型数据的更好替代方案将是一个非常松散的数据库模式。我仍然会遇到将其保存为 xml(或其他序列化格式)并使用它来生成视图并为自定义保存验证规则的麻烦,ModelBinder以便您可以更好地控制每个字段的布局和验证。

于 2009-05-29T17:04:17.967 回答
0

cottsak 的回答很吸引人。

至少有两个客户端 XForms 引擎。这是一个:

https://community.emc.com/community/edn/xmltech

于 2009-07-30T18:29:26.307 回答
-1

与直接生成带有“Web Forms 2.0”模型的 HTML 控件列表(如List<Tuple<Meta, Value>>. 注意:在服务器端,无论如何您都必须手动解析结果以使其适合您的结构。

搜索“下一层抽象”有利于快速开发,但请注意,“生成代码”(运行时或构建时)有其自身的特点。通常“较低层”的生成代码是比“较高抽象层”代码更好的解决方案。

因此,只需编写在 @Foreach 循环中生成 Web 2 控件的代码。

于 2015-08-24T14:33:53.787 回答