2

我在我的 MVC 应用程序中 使用了Simon Ince 创建的自定义RequiredIf 属性。

我有一个视图模型,它像这样传递给视图:

public class HistoryViewModel
{
    public Contact ContactModel { get; set; }
    public Account AccountModel { get; set; }
    public Person PersonModel { get; set; }
 }

我有一堆模型,其中包含所有道具(即contact.cs、account.cs、person.cs)

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public bool IsAdult { get; set; }        
    [RequiredIf("IsAdult", "Yes", Errormessage="Please leave a comment")]
    public string Comments { get; set; }
 }

通过视图模型时,RequiredIf验证似乎不起作用。如果我将属性直接移动到视图模型中,它工作正常. All other[必需]`属性通过视图模型从模型中工作。

那么我是否需要将所有需要RequiredIf 的属性移到视图模型中?或者还有其他方法可以解决这个问题吗?

4

2 回答 2

6

您是否尝试过MVC FoolProof 验证?它通过客户端支持支持RequiredIf(以及其他一些)。有时它可能有点错误,但最新版本还不错。

以与上述相同的方式工作(我在切换到 FoolProof 之前尝试过)

   public bool Married { get; set; }

    [RequiredIfTrue("Married")]
    public string MaidenName { get; set; }
于 2012-09-27T23:54:16.197 回答
2

问题

如果您查看 razor 视图发出的 html,您会发现这里有什么问题。

如果没有 viewmodel,我们应该生成以下代码(稍微清理一下):

<input type="checkbox" name="IsAdult" id="IsAdult" />
<input type="text" name="Comments" id="Comments"
       data-val-requiredif-dependprop="IsAdult" 
       data-val-requiredif-value="Yes"
       data-val-requiredif="Please leave a comment" />

使用 viewmodel,我们得到:

<input type="checkbox" name="PersonModel.IsAdult" id="PersonModel_IsAdult" />
<input type="text" name="PersonModel.Comments" id="PersonModel_Comments"
       data-val-requiredif-dependprop="IsAdult" 
       data-val-requiredif-value="Yes"
       data-val-requiredif="Please leave a comment" />

每当一个属性嵌套在另一个属性中时,MVC 都会构建一个前缀堆栈来生成唯一的 ID 和名称。您可以在第一种情况下看到,IsAdult足以识别字段,但是一旦嵌套,id 就发生了变化。在不显眼的验证(存储在属性中的规则)中,我们可能需要知道的关于如何验证元素的所有内容都需要在 中发送data-attributes,包括如何定位其他属性。

解决方案

类上的 data 属性永远不应该知道或关心它在什么上下文下被调用,所以它会继续盲目地指出它所依赖的相对属性:

public bool IsAdult { get; set; }
[RequiredIf("IsAdult", "Yes", Errormessage="Please leave a comment")]
public string Comments { get; set; }

所以我们必须在服务器或客户端上建立上下文。

在服务器上- 不!

作为您的一部分public class RequiredIfAttribute : ValidationAttribute, IClientValidatable,您将拥有一个发出客户端验证规则的方法,如下所示:

public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
    ModelClientValidationRule requiredIfRule = new ModelClientValidationRule();
    requiredIfRule.ErrorMessage = ErrorMessageString;
    requiredIfRule.ValidationType = "requiredif";
    requiredIfRule.ValidationParameters.Add("dependprop", this._propertyName);
    requiredIfRule.ValidationParameters.Add("value", Json.Encode(this._value));     

    yield return requiredIfRule;
}

这里的诱惑是导航viewContext.ViewData.TemplateInfo返回GetFullHtmlFieldId,但据我所知,此信息尚不可用

客户- 不稳定但工作:

在客户端,我们将使用如下所示的方法连接适配器:

$.validator.unobtrusive.adapters.add('requiredif', ['dependprop', 'value'], function (options) {
    options.rules["requiredif"] = { 
        id: '#' + options.params['dependprop'],
        value: JSON.parse(options.params.value) 
    };
    options.messages['requiredif'] = options.message;
});

请注意,这仍然只采用普通的旧属性名称,并假定它可以用作 ID 来定位对象。

通过做出一些合理的假设,我们可以构建完整的依赖属性 id。应该总是这样, requiredif 调用元素与我们识别的属性在同一范围内(这就是我们能够通过反射在服务器上找到它的方式)。

因此,我们将从发送者那里获取该上下文,删除该属性的名称,并将我们自己的名称添加到以下内容中$.validator.unobtrusive.adapters.add

var curId = options.element.id;               // get full id      i.e. ViewModel_Comments
var context = curId.replace(/[^_]+$/, "");    // remove last prop i.e. ViewModel_
var targetProp = options.params['dependprop'] // target name      i.e. IsAdult
var targetId = '#' + context + targetProp;    // build target ID  i.e. #ViewModel_IsAdult

options.rules["requiredif"] = {
    id: targetId,
    value: JSON.parse(options.params.value)
};

这应该有助于找到正确的客户端属性 - 然后写下您需要满足的任何其他条件。

于 2016-09-27T13:09:00.840 回答