将隐藏字段添加到模型时,MVC 范围属性失败
MVC Range Attribute fails when a hidden field is added to the model
我有一个包含 属性 的父模型,子模型使用它来预填充视图中的字段。
我想将 属性 移动到它所属的子模型中,但是当我这样做时另一个 属性 的范围属性失败。
当我有一个仅用于隐藏在 EditorTemplate 上的 属性 时,为什么范围属性验证失败?
模特长得像
public class ParentModel
{
public SubModel subModel { get; set; }
}
public class SubModel
{
public uint? DefaultValue { get; set; }
[Required]
[Range(1,100)]
public uint RangedId { get; set;}
public bool EnableRange { get; set; }
}
视图(EditorTemplate)看起来像
@model SubModel
@Html.HiddenFor(model => model.DefaultValue)
@Html.TextBoxFor(model => model.RangeId)
<script>
$('#EnableRange').change(function() {
if($('#EnableRange').val()){
// remove the disabled attribute from the RangeId Field
} else {
// add the disabled attribute from the RangeId Field
}
}
</script>
控制器看起来像
public ActionResult Create(TViewModel model)
{
try
{
if (ModelState.IsValid)
{
//Do Something Meaningful
}
//Redisplay the view
}
}
使用子模型中的默认值 属性,即使在表单上禁用了 RangeId,RangeId 的范围属性也会触发。这导致 ModelState.IsValid 为假。
当我将 DefaultValue 属性 向上移动到 ParentModel 时,RangeId 的 Range 属性不再触发(因为该字段已禁用)。这导致 ModelState.IsValid 为真,因为从不评估 RangeId 以进行验证。
无论你认为发生了什么,都没有发生。服务器端 Model.IsValid 不关心任何事情,也不会直接受到禁用客户端控件的影响(尽管它可能会受到间接影响,我们将在下面看到)。如果嵌套表单字段 posted 并且嵌套对象具有必需的属性,则始终会进行验证。
更可能的是,这里的真正问题是当您在子模型中有 DefaultValue 时,然后当您将模型提交给父模型时,模型绑定器会创建 SubModel 的实例,因为它包含 DefaultValue 的值。当您将它移动到父级并禁用 RangeId 时,post 没有值,因此不会创建 SubModel,因此不会发生验证。
也就是说,我的猜测是,当您将 DefaultValue 移至父级时,SubModel 在 post 后面为空,因此因为没有要验证的实例,所以没有任何验证失败,特别是因为您是不保留 EnableRange 值。
所以你确实有几个问题。首先,禁用客户端上的控件不会禁用服务器上的验证。其次,如果没有嵌套的表单字段 post 发送到服务器,则不会创建嵌套对象并且不会发生验证(因此在这方面,如果您非常喜欢,可以禁用验证作为副作用小心)。第三,如果您 post 一些嵌套表单字段而不是其他表单字段,那么将创建嵌套对象进行验证,并且将在未 post 编辑的字段上进行验证,因为它们已被禁用。
我有一个包含 属性 的父模型,子模型使用它来预填充视图中的字段。 我想将 属性 移动到它所属的子模型中,但是当我这样做时另一个 属性 的范围属性失败。 当我有一个仅用于隐藏在 EditorTemplate 上的 属性 时,为什么范围属性验证失败?
模特长得像
public class ParentModel
{
public SubModel subModel { get; set; }
}
public class SubModel
{
public uint? DefaultValue { get; set; }
[Required]
[Range(1,100)]
public uint RangedId { get; set;}
public bool EnableRange { get; set; }
}
视图(EditorTemplate)看起来像
@model SubModel
@Html.HiddenFor(model => model.DefaultValue)
@Html.TextBoxFor(model => model.RangeId)
<script>
$('#EnableRange').change(function() {
if($('#EnableRange').val()){
// remove the disabled attribute from the RangeId Field
} else {
// add the disabled attribute from the RangeId Field
}
}
</script>
控制器看起来像
public ActionResult Create(TViewModel model)
{
try
{
if (ModelState.IsValid)
{
//Do Something Meaningful
}
//Redisplay the view
}
}
使用子模型中的默认值 属性,即使在表单上禁用了 RangeId,RangeId 的范围属性也会触发。这导致 ModelState.IsValid 为假。 当我将 DefaultValue 属性 向上移动到 ParentModel 时,RangeId 的 Range 属性不再触发(因为该字段已禁用)。这导致 ModelState.IsValid 为真,因为从不评估 RangeId 以进行验证。
无论你认为发生了什么,都没有发生。服务器端 Model.IsValid 不关心任何事情,也不会直接受到禁用客户端控件的影响(尽管它可能会受到间接影响,我们将在下面看到)。如果嵌套表单字段 posted 并且嵌套对象具有必需的属性,则始终会进行验证。
更可能的是,这里的真正问题是当您在子模型中有 DefaultValue 时,然后当您将模型提交给父模型时,模型绑定器会创建 SubModel 的实例,因为它包含 DefaultValue 的值。当您将它移动到父级并禁用 RangeId 时,post 没有值,因此不会创建 SubModel,因此不会发生验证。
也就是说,我的猜测是,当您将 DefaultValue 移至父级时,SubModel 在 post 后面为空,因此因为没有要验证的实例,所以没有任何验证失败,特别是因为您是不保留 EnableRange 值。
所以你确实有几个问题。首先,禁用客户端上的控件不会禁用服务器上的验证。其次,如果没有嵌套的表单字段 post 发送到服务器,则不会创建嵌套对象并且不会发生验证(因此在这方面,如果您非常喜欢,可以禁用验证作为副作用小心)。第三,如果您 post 一些嵌套表单字段而不是其他表单字段,那么将创建嵌套对象进行验证,并且将在未 post 编辑的字段上进行验证,因为它们已被禁用。