有没有办法清除客户端上的模型状态错误?

时间:2018-03-30 05:23:48

标签: asp.net-mvc

我有一个包含子对象列表的模型。我已经创建了一个自定义验证属性,在模型上实现了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);
    }

1 个答案:

答案 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}.jsjquery.validate.js脚本之后

jquery.validate.unobtrusive.js