我有一个包含子对象列表的模型。我已经创建了一个自定义验证属性,在模型上实现了IValidatableObject,并且我收到了预期的错误消息。问题是,一旦属性在模型状态中出错,我就无法获得更新的值以回发到服务器。在点击提交按钮和在控制器中接收模型之间,它们会被清除一段时间。
如果我在控制器动作中调用ModelState.Clear(),我不会收到任何消息,但新值会按预期发布。然而,该模型在自定义属性上获取,因为ModelState.IsValid == false
我认为处理这个问题的最好方法是在$(准备好)之后以某种方式调用客户端上的ModelState.Clear(),这样我就可以得到验证消息,但也可以将更改后的值发布到服务器上。这有可能还是有更好的方法来做到这一点?
父模型
public class PayrollPlanModel : IMapFrom<Data.PayrollPlan>
{
public int? PayrollPlanId { get; set; }
[Required]
public string Name { get; set; }
public List<PlanOptionFormModel> Options { get; set; }
}
具有自定义属性
的父级上的模型列表属性public class PlanOptionFormModel : IValidatableObject
{
public int PlanOptionValueId { get; set; }
public int PayrollPlanId { get; set; }
public string PlanName { get; set; }
public int PlanOptionId { get; set; }
public string Description { get; set; }
[UIHint("_Money")]
[RequiredIf("Selected", true)]
public decimal? Value { get; set; }
public bool Selected { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Selected && !Value.HasValue)
{
yield return new ValidationResult("Add a value.");
}
}
}
自定义属性(从这里无耻地被盗)
public class RequiredIfAttribute : ValidationAttribute
{
RequiredAttribute _innerAttribute = new RequiredAttribute();
public string _dependentProperty { get; set; }
public object _targetValue { get; set; }
public RequiredIfAttribute(string dependentProperty, object targetValue)
{
this._dependentProperty = dependentProperty;
this._targetValue = targetValue;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var field = validationContext.ObjectType.GetProperty(_dependentProperty);
if (field != null)
{
var dependentValue = field.GetValue(validationContext.ObjectInstance, null);
if ((dependentValue == null && _targetValue == null) || (dependentValue.Equals(_targetValue)))
{
if (!_innerAttribute.IsValid(value))
{
string name = validationContext.DisplayName;
return new ValidationResult(ErrorMessage = name + " Is required.");
}
}
return ValidationResult.Success;
}
else
{
return new ValidationResult(FormatErrorMessage(_dependentProperty));
}
}
}
网页摘要
for (int i = 0; i < Model.Options.Count; i++)
{
<div class="row">
<div class="col-md-3">
@Html.HiddenFor(m => Model.Options[i].PlanOptionValueId)
@Html.HiddenFor(m => Model.Options[i].PayrollPlanId)
@Html.HiddenFor(m => Model.Options[i].PlanOptionId)
@Html.HiddenFor(m => Model.Options[i].Description)
</div>
<div class="col-md-1 text-right">
@Html.CheckBoxFor(m => Model.Options[i].Selected, new { @data_textbox = "optionValue_" + i.ToString(), @class = "form-control modelOptionSelector" })
</div>
<div class="col-md-2 text-right">
<h4>@Model.Options[i].Description</h4>
</div>
<div class="col-md-1">
@Html.EditorFor(m => Model.Options[i].Value, Model.Options[i].Selected ? new { HtmlAttributes = new { id = "optionValue_" + i.ToString(), @class = "planOptionValueEditor" } } : (object)new { HtmlAttributes = new { disabled = "disabled", id = "optionValue_" + i.ToString(), @class = "planOptionValueEditor" } })
@Html.ValidationMessageFor(m => Model.Options[i].Value)
</div>
</div>
}
<br />
编辑模板
@model decimal?
@{
var defaultHtmlAttributesObject = new { };
var htmlAttributesObject = ViewData["htmlAttributes"] ?? new { };
var htmlAttributes = Html.MergeHtmlAttributes(htmlAttributesObject, defaultHtmlAttributesObject);
string attemptedValue = "";
ModelState modelStateForValue = Html.ViewData.ModelState[Html.IdForModel().ToString()];
if (modelStateForValue != null)
{
attemptedValue = modelStateForValue.Value.AttemptedValue;
}
}
@(Html.Kendo().CurrencyTextBoxFor(m => m)
.HtmlAttributes(htmlAttributes)
.Format("c")
.Spinners(false)
)
控制器
[HttpPost]
public ActionResult EditPlan(PayrollPlanModel model)
{
if(ModelState.IsValid)
{
}
else
{
}
return View(model);
}
答案 0 :(得分:1)
尝试从客户端清除ModelState
错误是没有意义的。当您向方法发出请求时,ModelState
仅在控制器方法中设置(由DefaultModelBinder
)。在任何情况下,您的问题都与ModelState
在控制器方法中无效或无效有关。
您需要对代码进行一些更改:
您应该删除EditorTemplate
decimal?
这意味着该类型的任何属性都将使用该模板。而是替换你的
@Html.EditorFor(m => Model.Options[i].Value, ...)
带
@(Html.Kendo().CurrencyTextBoxFor(m => m.Options[i].Value)....
在主视图中。
如果您确实想要使用模板,请将其设为命名模板(使用@Html.EditorFor(m => Model.Options[i].Value, "yourTemplateName")
进行调用),但无论如何,您需要删除与attemptedValue
和{相关的代码{1}}(包括modelStateForValue
块) - if
方法将始终正确使用EditorFor()
中的值(如果存在)。
接下来,您的ModelState
未实施RequiredIfAttribute
,因此您无法获得客户端验证。您可以使用foolproof库,或者如果您想编写自己的库,请参考this answer以获取IClientValidatable
的完整实现,包括客户端验证的脚本。
接下来,您需要从模型中删除RequiredIfAttribute
实现(IValidatableObject
方法)。这只是重复Validate()
属性正在进行的验证,您应该避免将[RequiredIf]
与ValidationAttribute
混合(有关详细信息,请参阅The Complete Guide To Validation In ASP.NET MVC 3 - Part 2)
最后,IValidatableObject
方法隐藏输入并呈现自己的html。默认情况下,未验证隐藏的输入,因此您需要重新配置验证器。在主视图中,添加以下脚本(在Kendo().CurrencyTextBoxFor()
,jquery-{version}.js
和jquery.validate.js
脚本之后
jquery.validate.unobtrusive.js