在ASP.Net MVC3中创建条件表单的正确方法是什么?

时间:2011-08-15 11:30:05

标签: asp.net-mvc-3

问题概述:

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

我的实际问题:

让我们从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个标签组成的相当大的表单。在第3个选项卡上,我显示基于Response模型的局部视图。这是我想要条件逻辑的地方。 ResponseType属性是表单上的单选按钮。它可以有两个值:NORMAL和ERROR。在出现错误的情况下,我想显示并验证ErrorCodeErrorDescr属性。在NORMAL的情况下,我想只显示和验证State属性。

我的解决方案:

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

    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
       }
    }
    
  4. 这是一大堆丑陋的管道(尤其是控制器代码 - 使用字符串键从ModelState中删除项目......没有类型安全等),肯定必须有更好的方法吗? < / p>

2 个答案:

答案 0 :(得分:1)

您可以尝试继承IValidateableObject课程中的Response并在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...
    }
}

答案 1 :(得分:0)

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

正确的解决方案是实现自定义验证属性,并且还实现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,对其进行配置后,该属性在服务器端和客户端均可以使用。您可以在项目页面上阅读详细信息。