ASP.NET MVC 中动态表单的建议

9
我正在处理在ASP.NET MVC视图中渲染动态表单的工作,满足以下要求:
  • 字段可以被验证
  • 当表单无效时状态得到保留
我打算创建自定义模型绑定器来实现这一点。通常的计划如下:
  1. 使用这些属性定义表单字段
    • 提示(字段旁边的标签)
    • 类型(文本、复选框列表、单选按钮列表等)
    • 选择项(用于列表字段)
    • 是否必填
    • 正则表达式(用于文本字段)
    • 显示选项
    • 从控制器向视图发送字段定义集合
    • 将字段呈现为HTML并发送到浏览器
    • 将表单发送回服务器
    • 自定义模型绑定器将表单绑定到包含提交的值的字段定义集合中
    • 验证每个字段
    • 如果需要-必须有一个值
    • 如果RegEx-必须匹配
    • 对于每个无效字段,都会向modelstate添加错误消息
    • 控制器决定该做什么
    • 如果所有字段都有效
      • 对字段及其值进行操作
    • 如果1个或多个字段无效
      • 将字段集合发送回视图
      • 再次呈现字段,使用它们以前尝试的值
      • 显示验证摘要
我不确定是否以最佳或最简单的方式处理此操作。这种方法会给我带来很多问题吗?我可以做些什么来改进它?
4个回答

8

我写了一个类库,基本上实现了我在问题描述中的伪代码。它运行得非常好。

编辑:

我终于清理了我的类库。我添加了一些新功能,并创建了一个相当完整文档的演示Web应用程序。

所有这些都托管在CodePlex上。希望这能帮助到某个人。


我认为我已经分享了这个想法。但是,我也不介意分享代码。稍等一下,我会给你一个链接。 - Ronnie Overby
http://mvcdynamicforms.codeplex.com - 只有代码,没有示例和文档。我会尽快发布文档和示例。 - Ronnie Overby
谢谢,即使没有文档,代码也非常直观,确实帮助我解决了相关问题。+1 - David Lay
很高兴能帮上忙。这个库的源代码确实很混乱。我这周一直在重构和清理它。我还创建了一个基本的演示MVC应用程序。我应该能够在这个周末将我的新工作发布到CodePlex网站上。 - Ronnie Overby
太棒了。我真的会尽快发布这个更干净的代码。 - Ronnie Overby
我如何在自定义模型中添加Form属性?在提交表单时,我的控制器总是返回null... - Andrei

0

虽然我不是专家,但我不得不创建一个解决方案,其中我的主要对象具有值列表。我们称之为Object A具有映射在数据库中的ApplicationValues列表。ApplicationValues具有Key(表单字段,例如PhoneNumber)和Value。

由于ApplicationValues是EntitySet,因此我必须创建get和set方法以正确处理设置特定的ApplicationValue。我还在我的数据库中拥有ApplicationRules列表,定义了这些应用程序值可以采用什么样的规则。

以下是一段代码片段,可能会帮助您开发符合您需求的解决方案。

public partial ApplicationValue
{
    public string Key;
    public string Value;
}

public partial ApplicationRule
{
    public string ValidationFormat;
    public string ValidationError;
    public bool Required;
}

public partial class A
{
    public void SetValue(string key, string value)
    {
        //ApplicationValues is the list of values associated to object A
        ApplicationValue v = ApplicationValues.SingleOrDefault
        (k => k.Key == key);

        //if we already have this value
        if (v != null)
        {   //...then we can simply set and return
            v.Value = value;
            return;
        }

        //else we need to create a new ApplicationValue
        v = new ApplicationValue
            {
                AffinityID = this.ID,
                Key = key,
                Value = value
            };

        ApplicationValues.Add(v);
    }

    public string GetValue(ApplicationField key)
    {
        return GetValue(key, String.Empty);
    }

    public string GetValue(ApplicationField key, string defaultValue)
    {
        if (ApplicationValues == null)
            return defaultValue;

        ApplicationValue value = ApplicationValues.SingleOrDefault
        (f => f.Key == key.ToString());

        return (value != null) ? value.Value : defaultValue;
    }

接着为了进行表单验证,我会遍历ApplicationRules(定义字段是否必填、包含正则表达式等),并将其与FormCollection匹配。

public ActionResult Details(FormCollection form)
{
    IList<ApplicationRule> applicationRules = //get my rules from the DB

    if (!(ValidateApplication(applicationRules, form, a)))
    {
        ModelState.AddModelError("message", "Please review the errors below.");
        return View(a);
    }
    ...
}

private bool ValidateApplication(IList<ApplicationRule> applicationRules,
                                 FormCollection form, A a)
    {
        //loop through the application rules
        foreach (ApplicationRule ar in applicationRules)
        {
            //try and retrieve the specific form field value using the key
            string value = form[ar.Key];

            if (value == null)
                continue;

            //set the model value just in case there is an error
            //so we can show error messages on our form
            ModelState.SetModelValue(ar.Key, ValueProvider[ar.Key]);

            //if this rule is required
            if (ar.Required)
            {   //...then check if the field has a value
                if (String.IsNullOrEmpty(value))
                {
                    ModelState.AddModelError(ar.Key, "Field is required");
                    continue;
                }
            }

            //if this rule has a validation format
            if (!String.IsNullOrEmpty(ar.ValidationFormat))
            {   //...then check the value is of the correct format
                Regex re = new Regex(ar.ValidationFormat);

                if (!re.IsMatch(value))
                {
                    ModelState.AddModelError(ar.Key, ar.ValidationError);
                    continue;
                }
            }

            a.SetValue(ar.Key, value);
        }

        return ModelState.IsValid;
    }

0

我并不是一个专家,但如果您对ASP.NET MVC非常陌生,那么我建议您先使用内置功能,而不是自己编写代码。它能够完成您所描述的大部分工作,但不鼓励在控制器中定义/构造UI,因为这是视图的工作。

通过ModelStateDictionary,您可以添加模型错误并设置模型值,这些值将在验证失败时绑定到表单输入中。

更新: 另一种看待它的方式:问自己为什么要使用MVC而不是经典的ASP.NET构建技术,然后看看您提出的方法是否与这些原因相符。对我来说,关注点分离是一个非常重要的原因,以及对生成的HTML具有粒度控制能力,我觉得您的方法可能会破坏这些东西。

特别针对您的编辑:

步骤1到3违背了MVC范例。 第4步可以接受。 步骤5到7是标准MVC实践,并得到框架的充分支持。例如,执行简单验证(C#)显示了验证和错误消息呈现的示例。


是的,但这仅适用于使用HtmlHelper创建的字段输入,并且我需要呈现的许多类型的字段不受支持(复选框列表,单选按钮列表,列表框) 。 - Ronnie Overby
确实存在这些困难。我会扩展HtmlHelper以便它可以正确支持它们,代码并不是很复杂。目前我使用自己的Utility类来处理它们,因为我还没有找到更高级的方法。我真的不理解MVC中为什么缺乏对这些输入类型的适当支持 :[ - D'Arcy Rittich
针对您的更新:我已经问过那个问题了。我使用MVC,因为我的应用程序99%非常适合MVC。但是这个应用程序的一小部分需要动态生成表单。我还不熟悉混合WebForms和MVC,所以我想尝试在MVC中完成这个任务。此外,我不认为我计划做的任何事情会破坏SOC或HTML控制权。 - Ronnie Overby
关于 SOC,我假设你的第三步是你编写的代码,通过自定义模型绑定器并自动为表单字段生成所有 HTML。这不是你的意思吗? - D'Arcy Rittich
不,我的计划并不是让模型绑定器与HTML渲染有任何关系。相反,FieldDefinition(步骤1)对象将负责呈现其HTML。此外,我计划从视图模板中调用FieldDefiniton的RenderHtml方法。 - Ronnie Overby
此外,我在这里涉足了一个有点棘手的领域 - 有些人喜欢生成的HTML代码,而有些人则更喜欢手工制作,只在必要时通过代码插入最小值。我倾向于后者。ASP.NET MVC确实为前者提供了一些很好的脚手架,我实际上并不介意,而我讨厌Web Forms。但是我最喜欢ASP.NET MVC的地方是你可以用多种方式做事情,它不会强迫自己的议程。 - D'Arcy Rittich

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接