问题 如何使用@ Html.ValidationMessageFor显示多个验证错误?


我可以看到我的 ModelState.Values[x].Errors 正确填充了单个属性的两个验证错误。

如果我使用 @Html.ValidationSummary() 在我看来,它正确地显示了两个错误....虽然在页面顶部,而不是在违规输入旁边。

运用 @Html.ValidationMessageFor(model => model.MyProperty) 显示 仅第一个错误 对于那个属性!

如何在适当的输入旁边显示两个错误?


6940
2017-11-22 07:08


起源



答案:


一种解决方案是在HtmlHelper上实现自己的扩展方法,该方法执行与默认ValidationMessageFor行为不同的操作。下面的示例@ Html.ValidationMessagesFor方法将连接已添加到ModelState的多个错误消息 服务器端 验证(仅)。

public static MvcHtmlString ValidationMessagesFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes = null)
{
    var propertyName = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).PropertyName;
    var modelState = htmlHelper.ViewData.ModelState;

    // If we have multiple (server-side) validation errors, collect and present them.
    if (modelState.ContainsKey(propertyName) && modelState[propertyName].Errors.Count > 1)
    {
        var msgs = new StringBuilder();
        foreach (ModelError error in modelState[propertyName].Errors)
        {
            msgs.AppendLine(error.ErrorMessage);
        }

        // Return standard ValidationMessageFor, overriding the message with our concatenated list of messages.
        return htmlHelper.ValidationMessageFor(expression, msgs.ToString(), htmlAttributes as IDictionary<string, object> ?? htmlAttributes);
    }

    // Revert to default behaviour.
    return htmlHelper.ValidationMessageFor(expression, null, htmlAttributes as IDictionary<string, object> ?? htmlAttributes);
}

如果您要在属于模型的集合中应用自定义业务验证(例如,交叉检查总计),或者将模型作为整体检查,则此功能非常有用。

使用此示例,其中Order.LineItems的集合在服务器端验证:

@using MyHtmlHelperExtensions    // namespace containing ValidationMessagesFor
@using (Html.BeginForm())
{
    @Html.LabelFor(m => m.LineItems)
    <ul>
        @Html.EditorFor(m => m.LineItems)
    </ul>
    @Html.ValidationMessagesFor(m => m.LineItems)
}

10
2017-11-23 08:07



好的解决方案这是否适用于具有viewmodels的嵌套视图?我不得不添加以下内容: var relativePropertyName = ExpressionHelper.GetExpressionText(expression);  var propertyName = String.Format("{0}{2}{1}", htmlHelper.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix, relativePropertyName, (String.IsNullOrWhiteSpace(relativePropertyName) ? String.Empty : ".")); - Fiddles
使其与嵌套视图一起使用的更简单方法是:propertyName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(propertyName) - Mark Shapiro
优秀的答案,它的工作原理。除了我的错误都在同一行。我需要每条错误消息都在它自己的行上。例如,中间有一个<br>标签 - theB3RV
@ theB3RV或者你可以使用 white-space: pre-wrap; CSS。 - Frank Sebastià


答案:


一种解决方案是在HtmlHelper上实现自己的扩展方法,该方法执行与默认ValidationMessageFor行为不同的操作。下面的示例@ Html.ValidationMessagesFor方法将连接已添加到ModelState的多个错误消息 服务器端 验证(仅)。

public static MvcHtmlString ValidationMessagesFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes = null)
{
    var propertyName = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).PropertyName;
    var modelState = htmlHelper.ViewData.ModelState;

    // If we have multiple (server-side) validation errors, collect and present them.
    if (modelState.ContainsKey(propertyName) && modelState[propertyName].Errors.Count > 1)
    {
        var msgs = new StringBuilder();
        foreach (ModelError error in modelState[propertyName].Errors)
        {
            msgs.AppendLine(error.ErrorMessage);
        }

        // Return standard ValidationMessageFor, overriding the message with our concatenated list of messages.
        return htmlHelper.ValidationMessageFor(expression, msgs.ToString(), htmlAttributes as IDictionary<string, object> ?? htmlAttributes);
    }

    // Revert to default behaviour.
    return htmlHelper.ValidationMessageFor(expression, null, htmlAttributes as IDictionary<string, object> ?? htmlAttributes);
}

如果您要在属于模型的集合中应用自定义业务验证(例如,交叉检查总计),或者将模型作为整体检查,则此功能非常有用。

使用此示例,其中Order.LineItems的集合在服务器端验证:

@using MyHtmlHelperExtensions    // namespace containing ValidationMessagesFor
@using (Html.BeginForm())
{
    @Html.LabelFor(m => m.LineItems)
    <ul>
        @Html.EditorFor(m => m.LineItems)
    </ul>
    @Html.ValidationMessagesFor(m => m.LineItems)
}

10
2017-11-23 08:07



好的解决方案这是否适用于具有viewmodels的嵌套视图?我不得不添加以下内容: var relativePropertyName = ExpressionHelper.GetExpressionText(expression);  var propertyName = String.Format("{0}{2}{1}", htmlHelper.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix, relativePropertyName, (String.IsNullOrWhiteSpace(relativePropertyName) ? String.Empty : ".")); - Fiddles
使其与嵌套视图一起使用的更简单方法是:propertyName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(propertyName) - Mark Shapiro
优秀的答案,它的工作原理。除了我的错误都在同一行。我需要每条错误消息都在它自己的行上。例如,中间有一个<br>标签 - theB3RV
@ theB3RV或者你可以使用 white-space: pre-wrap; CSS。 - Frank Sebastià


Dave A-W提供的答案基本上是正确的,但并不适用于所有情况。要获取正确的属性名称,MVC 3的源代码使用:

var propertyName = ExpressionHelper.GetExpressionText(expression);

这会处理表单数据中的任何前缀


3
2017-12-12 14:57