【问题标题】:Is there a way to clear modelstate errors on the client?有没有办法清除客户端上的模型状态错误?
【发布时间】:2018-03-30 05:23:48
【问题描述】:

我有一个带有子对象列表的模型。我创建了一个自定义验证属性,在模型上实现了 IValidatableObject,并且我收到了预期的错误消息。问题是,一旦属性在模型状态中出现错误,我就无法将更新的值发回服务器。在点击提交按钮和在控制器中接收模型之间的一段时间内,它们会被清除。

如果我在控制器操作中调用 ModelState.Clear(),我不会收到任何消息,但新值会按预期发布。然而,模型正在使用自定义属性,因为 ModelState.IsValid == false

我认为处理这个问题的最好方法是在 $(ready) 之后以某种方式在客户端上调用 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));
        }
    }
}

页面sn-p

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);
    }

【问题讨论】:

  • ModelState 是服务器端 - 从客户端调用它是没有意义的。你声称的没有意义。显示您的代码并解释问题所在。
  • 我添加了代码。你不明白的问题怎么办?一旦错误被添加到视图状态,该属性将不会将任何更新的值发布到控制器。我试图想一个更好的方法来解释它。给我一分钟
  • 一开始您似乎并不了解客户端和服务器端代码之间的区别。而且您不应该使用IValidatable 对象(这不会提供客户端验证,您的RequiredIfAttribute 也不会,因为您还没有实现IClientValidatable。给我一个机会来尝试并根据您的编辑。
  • 我理解他们很好。谢谢。我不明白为什么当添加模型状态错误时,该值停止发布到服务器也不是没有的,但一旦我实现了接口,那就是错误消息开始出现在客户端上的时候。耸耸肩
  • 实际上您可能对界面是正确的。客户端消息不是客户端返回的消息

标签: asp.net-mvc


【解决方案1】:

尝试从客户端清除 ModelState 错误是没有意义的。 ModelState 仅在您向方法发出请求时在控制器方法中设置(由DefaultModelBinder)。无论如何,您的问题与 ModelState 在控制器方法中的有效或无效无关。

您需要对代码进行一些更改:

您应该删除您的EditorTemplatedecimal? 这意味着该类型的任何属性都将使用该模板。而是替换您的

@Html.EditorFor(m => Model.Options[i].Value, ...)

@(Html.Kendo().CurrencyTextBoxFor(m => m.Options[i].Value)....

在主视图中。

如果你真的想使用一个模板,那就让它成为一个命名模板(使用@Html.EditorFor(m =&gt; Model.Options[i].Value, "yourTemplateName")调用,但无论如何,你需要删除与attemptedValuemodelStateForValue相关的代码(包括if 块) - EditorFor() 方法将始终正确使用来自 ModelState 的值(如果存在)。

接下来,您的RequiredIfAttribute 没有实现IClientValidatable,因此您将无法获得客户端验证。您可以使用 foolproof 库,或者如果您想编写自己的库,请参阅 this answer 以获取 RequiredIfAttribute 的完整实现,包括用于客户端验证的脚本。

接下来,您需要从模型中删除 IValidatableObject 实现(Validate() 方法)。这只是重复[RequiredIf] 属性正在执行的验证,您应该避免将ValidationAttributeIValidatableObject 混合使用(有关更多详细信息,请参阅The Complete Guide To Validation In ASP.NET MVC 3 - Part 2)。

最后,Kendo().CurrencyTextBoxFor() 方法隐藏了输入并呈现了自己的 html。默认情况下,隐藏的输入不会被验证,因此您需要重新配置验证器。在主视图中,添加以下脚本(在jquery-{version}.jsjquery.validate.jsjquery.validate.unobtrusive.js 脚本之后

<script>
    $.validator.setDefaults({ 
        ignore: [] 
    });
    .... // other scripts as required
<script>

【讨论】:

    猜你喜欢
    • 2011-12-22
    • 1970-01-01
    • 2021-12-14
    • 1970-01-01
    • 2020-11-03
    • 2020-09-09
    • 2012-04-01
    • 1970-01-01
    • 2016-01-06
    相关资源
    最近更新 更多