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

发布于 2024-07-25 07:52:39 字数 868 浏览 6 评论 0原文

我正在努力在 ASP.NET MVC 视图中渲染动态表单,以满足以下要求:

  • 可以验证字段
  • 表单无效时保留状态

我正在考虑创建一个自定义模型绑定器来实现此目的。 我通常计划这样做:

  1. 表单字段是使用这些属性定义的
    • 提示(字段旁边的标签)
    • 类型(文本、复选框列表、单选列表等)
    • 选项(列表字段)
    • 为必填
    • 正则表达式(用于文本字段)
    • 显示选项
    • 字段定义的集合从控制器发送到视图
    • 字段呈现为 HTML 并发送到浏览器
    • 表单被发送回服务器
    • 自定义模型绑定器将表单绑定到现在包含提交值的字段定义集合
    • 每个字段都经过验证
    • 如果需要 -> 必须有一个值
    • 如果正则表达式 -> 必须匹配
    • 对于每个无效字段,都会向模型状态添加一条错误消息
    • 控制器决定做什么
    • 如果所有字段均有效
      • 对字段及其值进行任何操作
    • 如果 1 个或多个字段无效
      • 将字段集合发送回视图
      • 使用之前尝试的值再次渲染字段
      • 显示验证摘要

我不确定我是否以最佳或最简单的方式执行此操作。 这种方法会给我带来很多问题甚至不起作用吗? 我可以做些什么来改进它?

I am working on rendering a dynamic form in an ASP.NET MVC view that will meet these requirements:

  • Fields can be validated
  • State is preserved when the form is invalid

I am looking into creating a custom model binder to achieve this. I am generally planning to do this:

  1. Form fields are defined with these properties
    • Prompt (label next to fields)
    • Type (text, checkboxlist, radiolist, etc.)
    • Choices (for list fields)
    • IsRequired
    • RegularExpression (for text fields)
    • Display Options
    • Collection of field definitions are sent from the controller to the view
    • Fields are rendered into HTML and sent to the browser
    • Form is sent back to the server
    • A custom model binder binds the form to a collection of field definitions that now contains the submitted values
    • Each field is validated
    • If required -> must have a value
    • If RegEx -> must match
    • For each invalid field, an error message is added to modelstate
    • The controller decides what to do
    • If all fields are valid
      • Do whatever with the fields and their values
    • If 1 or more fields are invalid
      • Send the collection of fields back to the view
      • Render the fields again, with their previously attempted values
      • Show the validation summary

I'm not sure if I am doing this in the best or easiest way. Will this approach give me a lot of problems or even work? What can I do to improve upon it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

夏末染殇 2024-08-01 07:52:39

我编写了一个类库,它基本上完全符合我的问题中所描述的伪代码。 效果很好。

编辑:

我终于开始清理我的班级图书馆了。 我添加了一些新功能并创建了一个文档相当完善的演示 Web 应用程序。

所有这些都托管在 CodePlex 上。 我希望这可以帮助别人。

I wrote a class library that basically does exactly what my psuedocode in my question describes. It works great.

EDIT:

I finally got around to cleaning up my class library. I have added some new features and created a fairly well documented demo web application.

All of this is hosted here on CodePlex. I hope this helps someone.

可爱暴击 2024-08-01 07:52:39

我绝不是专家,但如果您对 ASP.NET MVC 非常陌生,那么我建议您先从内置功能开始,然后再自行构建。 它执行您所描述的大部分内容,但不鼓励在控制器中定义/构造 UI,因为这是视图的工作。

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

更新:另一种看待它的方法:问问自己为什么使用 MVC 而不是经典的 ASP.NET 构建技术,然后看看您提出的方法是否符合这些原因。 对我来说,关注点分离以及对生成的 HTML 的精细控制是一个重要原因,我觉得你的方法可能会颠覆这些事情。

具体解决您的编辑问题:

步骤 1 到 1 均违反 MVC 范例。
第4步,好的。
步骤 5 到 7 几乎是标准的 MVC 实践,并且得到框架的完全支持。 例如,执行简单验证 (C#) 显示了验证示例以及错误信息的呈现。

I am by no means an expert, but if you are very new to ASP.NET MVC, then I recommend you start with the built-in functionality before rolling your own. It does most of what you have described, except does not encourage UI to be defined/constructed in the controller, since that is the job of the view.

Through the ModelStateDictionary you can add model errors and set model values which will then get bound to your form inputs on validation fail.

Update: Another way to look at it: ask yourself why you are using MVC over classic ASP.NET construction techniques, and then see if your proposed approach aligns with those reasons. For me separation of concerns was a huge reason, as well as granular control over the HTML generated, and I feel like your approach may subvert those things.

To address your edit specifically:

Steps 1 through through are against the MVC paradigm.
Step 4, fine.
Steps 5 through 7 are pretty much standard MVC practice and fully supported by the framework. E.g., Performing Simple Validation (C#) shows example of validation and presentation of error messages.

东京女 2024-08-01 07:52:39

虽然不是专家,但我必须创建一个解决方案,其中我的主要对象有一个值列表。 我们称之为对象 A,它具有映射到数据库中的 ApplicationValue 列表。 ApplicationValues 有一个键(表单字段,例如电话号码)和值。

由于 ApplicationValues 是一个 EntitySet,我必须创建 get 和 set 方法来正确处理特定 ApplicationValue 的设置。 我的数据库中还有一个应用程序规则列表,它定义了这些应用程序值可以采用的内容。

这是一段代码,可以帮助您开发满足您需求的解决方案。

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;
    }

Although not an expert I had to create a solution where my main object had a list of values. Lets call it Object A has a list of ApplicationValues that are mapped in the database. The ApplicationValues have a Key (the form field e.g. PhoneNumber) and the Value.

As the ApplicationValues were an EntitySet I had to create get and set methods to correctly handle setting a specific ApplicationValue. I also had a list of ApplicationRules in my database that defined what these application values could take.

Here is a snippet of code that may help you develop a solution for your needs.

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;
    }

And then to do the form validation I loop through ApplicationRules (which defines if a field is required, contains a regex etc) and match it to the 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;
    }
童话里做英雄 2024-08-01 07:52:39

您的字段定义有多动态? 如果它们不经常更改,您可以在创建定义后使用 code dom 生成模型和控制器。 我还没有在 ASP.NET MVC 中尝试过这个,但这可能是一个好方法。

http://msdn.microsoft.com/en-us/library/y2k85ax6。 aspx

本文使用code dom来生成ActionLink。

http://blogs.msdn.com/davidebb/archive/2009/06/01/a-buildprovider-to-simplify-your-asp-net-mvc-action-links.aspx#comments< /a>

How dynamic are your field definitions? If they dont change very often you could use code dom to generate the model and controller once the definition is created. I have not tried this in ASP.NET MVC but it might be a good way.

http://msdn.microsoft.com/en-us/library/y2k85ax6.aspx

This article uses code dom for ActionLink generation.

http://blogs.msdn.com/davidebb/archive/2009/06/01/a-buildprovider-to-simplify-your-asp-net-mvc-action-links.aspx#comments

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文