我在我的MVC应用中使用custom RequiredIf attribute created by Simon Ince。
我有一个viewmodel,它传递给视图,如下所示:
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
验证在通过viewmodel传递时似乎不起作用。如果我将属性直接移动到viewmodel . All other
[必需]`属性从模型到视图模型工作,它工作正常。
所以我需要将所有属性移动到viewmodel中,它们需要RequiredIf吗?或者还有另一种解决方法吗?
答案 0 :(得分:6)
你试过MVC FoolProof validation吗?它支持客户端支持的RequiredIf(以及其他一些人)。它有时可能有点儿马车,但最新版本并不坏。
以与上述相同的方式工作(我在切换到FoolProof之前尝试过)
public bool Married { get; set; }
[RequiredIfTrue("Married")]
public string MaidenName { get; set; }
答案 1 :(得分:2)
如果您查看剃刀视图发出的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
中发送,包括如何定位其他属性。
类的数据属性永远不应该知道或关心它被调用的上下文,因此它将继续盲目地指出它所依赖的相对属性:
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
,但据我所知,information isn't available yet。
客户 - 不知道但正在工作:
在客户端上,我们将使用看起来something like this的方法连接适配器:
$.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)
};
这应该有助于找到合适的客户端属性 - 然后编写您需要满足的任何其他条件。