【问题标题】:What is the right way for creating conditional forms in ASP.Net MVC3?在 ASP.Net MVC3 中创建条件表单的正确方法是什么?
【发布时间】:2011-08-15 11:30:04
【问题描述】:

问题概述:

我想在我的表单中加入一些条件逻辑。用户使用单选按钮进行选择,根据选择我想隐藏一些字段并显示其他一些字段。这也应该影响验证(不应该验证隐藏的字段)。我认为这是一个典型的问题,但我找不到任何示例,而且我自己的解决方案似乎有很多麻烦。

我的实际问题:

让我们从 viewmodel 类开始(为了这个问题的需要做了一些简化):

public class Scenario
{
   public Request Request { get; set; }
   public Response Response { get; set; }
   // … some other properties
}

public class Request
{
   //some properties
}

public class Response
{
    [Required]
    public string ResponseType { get; set; }

    [Required]
    public string State { get; set; }

    [Required]
    [NotZero] //this is my custom validation attribute
    public string ErrorCode { get; set; }

    public string ErrorDescr { get; set; }
}

Scenario 模型的创建/编辑视图中,我有一个由3 个选项卡组成的相当大的表单。在第三个选项卡上,我显示了基于 Response 模型的局部视图。这就是我想要条件逻辑的地方。 ResponseType 属性是表单上的单选按钮。它可以有两个值:NORMAL 和 ERROR。在 ERROR 的情况下,我想显示和验证 ErrorCodeErrorDescr 属性。在 NORMAL 的情况下,我只想显示和验证 State 属性。

我的解决方案:

  1. 在响应部分视图中,我有一些 jquery .hide() 和 .show() 调用连接到隐藏/显示相关的输入元素。
  2. 我修改了 jquery 不显眼的验证脚本以阻止它验证隐藏字段 (http://blog.waynebrantley.com/2011/01/mvc3-validates-hidden-fields-using.html)
  3. 在场景控制器中,我有这样的代码:

    public ActionResult Edit(int id, Scenario scenario)
    {
       Response response=scenario.Response;
       if (response.ResponseType != null)
       {
          if (response.ResponseType == "NORMAL")
          {
             //in this case remove validation for errorcode
             this.ModelState.Remove("Response.ErrorCode");
          }
          else
          {
             //in this case remove validation for state
             this.ModelState.Remove("Response.State");
          }
       }
       if (ModelState.IsValid)
       {
           //map to entity and save to database
       }
    }
    

这是一大堆丑陋的管道(尤其是控制器代码 - 使用字符串键从 ModelState 中删除项目...没有类型安全等),肯定有更好的方法吗?

【问题讨论】:

    标签: asp.net-mvc-3


    【解决方案1】:

    您可以尝试从您的Response 类中的IValidateableObject 继承并在Validate 中执行条件验证。像这样:

    public class Response : IValidateableObject
    {
        [Required]
        public string ResponseType { get; set; }
        public string State { get; set; }   
        public string ErrorCode { get; set; }
        public string ErrorDescr { get; set; }
    
        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            if (ResponseType == "NORMAL")
            {
                if (State.IsNullOrWhiteSpace)
                    yield return new ValidationResult("State is required", new [] { "State" });
            }
    
            // additional validations...
        }
    }
    

    【讨论】:

    • 不幸的是,以这种方式进行的验证在客户端不起作用。我真的不喜欢这样,因为它引入了两阶段验证效果(用户根据客户端验证正确填写表单,提交表单,嘿,他得到一些新的错误......糟糕的用户体验) .
    【解决方案2】:

    这已经很老了,我不喜欢这个没有答案的闲逛,所以我会自己回答。

    正确的解决方案是实现自定义验证属性,同时实现 IClientValidatable 接口。这满足了所有要求 - 控制器内部没有管道和一致的客户端验证。

    现在我什至可能不会自己实现这个 - 我会使用像 ExpressiveAnnotations 这样的现有库,它提供了一个很好且灵活的 RequiredIf 属性:

    public class Response
    {
        [Required]
        public string ResponseType { get; set; }
    
        [RequiredIf("ResponseType == 'NORMAL'"]
        public string State { get; set; }
    
        [RequiredIf("ResponseType == 'ERROR'"]
        public string ErrorCode { get; set; }
    
        public string ErrorDescr { get; set; }
    }
    

    通过向 Global.asax 添加几行并在验证器中包含 javascript 来配置该属性后,该属性在服务器端和客户端都有效。您可以阅读项目页面上的详细信息。

    【讨论】:

      猜你喜欢
      • 2012-09-12
      • 1970-01-01
      • 1970-01-01
      • 2014-08-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多