Custom ValidationSummary模板Asp.net MVC 3

时间:2011-05-02 13:40:00

标签: validationsummary asp.net-mvc-3

我正在开发一个使用Asp.Net MVC3的项目

在视图中我有@Html.ValidationSummary(true)并且通常会产生

<div class="validation-summary-errors">
    <ul>
        <li>Something bad Happened!</li>
    </ul>
</div>

如何将此ValidationSummary扩展到MyValidationSummary并生成如下所示的Html代码模板:

<div class="notification warning"> 
    <span></span> 
    <div class="text"> <p>Something bad Happened!</p> </div> 
</div>

7 个答案:

答案 0 :(得分:131)

我的方法是使用自定义ValidationSummary.cshtml

@model ModelStateDictionary

@if(!Model.IsValid)
{
    <div class="validation-summary-errors">
        <ul>
            @foreach (var modelError in 
                     Model.SelectMany(keyValuePair => keyValuePair.Value.Errors))
            {
                <li>@modelError.ErrorMessage</li>
            }
        </ul>
    </div>
}

将此部分视图放在您的共享文件夹中,并从您的代码中引用它:

@Html.Partial("_ValidationSummary", ViewData.ModelState);

这样你就可以完全控制你的HTML。

答案 1 :(得分:45)

question详细说明了编写自定义验证摘要的过程。

修改 这将做你想要的:

public static class LinqExt 
{
    public static string MyValidationSummary(this HtmlHelper helper, string validationMessage="")
    {
        string retVal = "";
        if (helper.ViewData.ModelState.IsValid)
            return "";

        retVal += "<div class='notification-warnings'><span>";
        if (!String.IsNullOrEmpty(validationMessage))
            retVal += helper.Encode(validationMessage);
        retVal += "</span>";
        retVal += "<div class='text'>";
        foreach (var key in helper.ViewData.ModelState.Keys) 
        {
            foreach(var err in helper.ViewData.ModelState[key].Errors)
                retVal += "<p>" + helper.Encode(err.ErrorMessage) + "</p>";
        }
        retVal += "</div></div>";
        return retVal.ToString();
    }
}

代码是自我解释的;只需枚举模型状态错误并将错误包装在您选择的dom元素中。如果我使用它,则会出现错误:

<%:Html.MyValidationSummary()%>

它会在页面上显示html标签作为文本而不是呈现它。

<%=Html.MyValidationSummary()%>

这很好用。

答案 2 :(得分:22)

flos's answer的基础上,我使它与微软的jQuery Unobtrusive Validation兼容,并添加了Bootstrap的3面板样式。这是新代码:

@model ModelStateDictionary

<div class="@(Html.ViewData.ModelState.IsValid ? "validation-summary-valid" : "validation-summary-errors") panel panel-danger"
     data-valmsg-summary="true">
    <div class="panel-heading">
        Please, correct the following errors:
    </div>
    <div class="panel-body">
        <ul>
            @foreach(var modelError in Model.SelectMany(keyValuePair => keyValuePair.Value.Errors))
            {
                <li>@modelError.ErrorMessage</li>
            }
        </ul>
    </div>
</div>

您可以在此详细阅读:

Creating a custom ASP.NET MVC @Html.ValidationSummary styled with Bootstrap 3 panel

我还创建了一个示例ASP.NET MVC项目,以显示此自定义ValidationSummary的运行情况。得到它:

https://github.com/leniel/AspNetMvcCustomHtmlValidationSummary

答案 3 :(得分:2)

只是在这里发布我的答案,因为它对我有用;)

我使用一个简单的扩展方法,它接受MvcHtmlString并将其解码回HTML:

    public static MvcHtmlString ToMvcHtmlString(this MvcHtmlString htmlString)
    {
        if (htmlString != null)
        {
            return new MvcHtmlString(HttpUtility.HtmlDecode(htmlString.ToString()));
        }
        return null;
    }

为了探测这个问题,我将验证摘要帮助器添加到我的chstml中,如下所示:

@Html.ValidationSummary(true).ToMvcHtmlString()

这意味着,我可以将自定义HTML添加到我的验证摘要中:

ModelState.AddModelError("", "<p>This message can have html in it</p>");

我甚至可以在我的字段验证消息中添加自定义HTML:

ModelState.AddModelError("MyField", "<p>This message can have html in it</p>");

要使我的字段验证消息与HTML一起使用:

@Html.ValidationMessageFor(model => model.MyField).ToMvcHtmlString();

答案 4 :(得分:1)

添加相关样式:

.field-validation-error {
    color: #b94a48;
}

.field-validation-valid {
    display: none;
}

input.input-validation-error {
    border: 1px solid #b94a48;
}

input[type="checkbox"].input-validation-error {
    border: 0 none;
}

.validation-summary-errors {
    color: #b94a48;
}

.validation-summary-valid {
    display: none;
}

答案 5 :(得分:1)

我只需要为服务器端验证做一些类似的事情(比如检查文件内容),最后完全篡夺了@ Html.ValidationSummary并完成了相当不错的小工作。

我们有一个扩展Controller的BaseController类,我们在内部覆盖OnActionExecuting方法用于多种目的。 我们在ViewBag中为我们的错误消息创建一个新列表,并确保在任何操作运行之前初始化它。然后我们可以添加我们要显示的错误,并在屏幕上显示。

出于这个问题的目的,它看起来像这样。

AGPBI: {"kind":"simple","text":"UNEXPECTED TOP-LEVEL EXCEPTION:","sources":[{}]}
AGPBI: {"kind":"simple","text":"java.lang.RuntimeException: Exception parsing classes","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main.processClass(Main.java:752)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main.processFileBytes(Main.java:718)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main.access$1200(Main.java:85)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main$FileBytesConsumer.processFileBytes(Main.java:1645)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.cf.direct.ClassPathOpener.processArchive(ClassPathOpener.java:284)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:166)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.cf.direct.ClassPathOpener.process(ClassPathOpener.java:144)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main.processOne(Main.java:672)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main.processAllFiles(Main.java:542)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main.runMultiDex(Main.java:366)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main.run(Main.java:275)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main.main(Main.java:245)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.Main.main(Main.java:106)","sources":[{}]}
AGPBI: {"kind":"simple","text":"Caused by: com.android.dx.cf.iface.ParseException: bad class file magic (cafebabe) or version (0034.0000)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.cf.direct.DirectClassFile.parse0(DirectClassFile.java:472)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.cf.direct.DirectClassFile.parse(DirectClassFile.java:406)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.cf.direct.DirectClassFile.parseToInterfacesIfNecessary(DirectClassFile.java:388)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.cf.direct.DirectClassFile.getMagic(DirectClassFile.java:251)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main.parseClass(Main.java:764)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main.access$1500(Main.java:85)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main$ClassParserTask.call(Main.java:1684)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main.processClass(Main.java:749)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\t... 12 more","sources":[{}]}
AGPBI: {"kind":"simple","text":"UNEXPECTED TOP-LEVEL EXCEPTION:","sources":[{}]}
AGPBI: {"kind":"simple","text":"java.lang.RuntimeException: Exception parsing classes","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main.processClass(Main.java:752)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main.processFileBytes(Main.java:718)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main.access$1200(Main.java:85)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main$FileBytesConsumer.processFileBytes(Main.java:1645)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.cf.direct.ClassPathOpener.processArchive(ClassPathOpener.java:284)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:166)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.cf.direct.ClassPathOpener.process(ClassPathOpener.java:144)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main.processOne(Main.java:672)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main.processAllFiles(Main.java:569)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main.runMultiDex(Main.java:366)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main.run(Main.java:275)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main.main(Main.java:245)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.Main.main(Main.java:106)","sources":[{}]}
AGPBI: {"kind":"simple","text":"Caused by: com.android.dx.cf.iface.ParseException: bad class file magic (cafebabe) or version (0034.0000)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.cf.direct.DirectClassFile.parse0(DirectClassFile.java:472)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.cf.direct.DirectClassFile.parse(DirectClassFile.java:406)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.cf.direct.DirectClassFile.parseToInterfacesIfNecessary(DirectClassFile.java:388)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.cf.direct.DirectClassFile.getMagic(DirectClassFile.java:251)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main.parseClass(Main.java:764)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main.access$1500(Main.java:85)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main$ClassParserTask.call(Main.java:1684)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\tat com.android.dx.command.dexer.Main.processClass(Main.java:749)","sources":[{}]}
AGPBI: {"kind":"simple","text":"\t... 12 more","sources":[{}]}
AGPBI: {"kind":"simple","text":"2 errors; aborting","sources":[{}]}

然后在我们的_Layout.cshtml中添加以下@RenderBody()

public class BaseController : Controller
{
    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (ViewBag.ErrorsList == null)
        {
            ViewBag.ErrorsList = new List<string>();
        }
    }
}

现在每当我们想要显示为验证错误消息的服务器端发生错误时,我们只需将其添加到ViewBag.ErrorsList

@if(ViewBag.ErrorsList.Count > 0)
{
    <div class="container margin-top-10 alert alert-danger">
        <h3><i class="glyphicon glyphicon-warning-sign"></i></h3><br/>
        @foreach (string error in @ViewBag.ErrorsList)
        {
            @error <br/>
        }
    </div>
    @RenderBody()
}

瞧,一个自定义容器,用于服务器端验证错误消息,包含您想要的任何样式,错误的传递方式与ValidationSummary相同。

答案 6 :(得分:0)

我想只显示顶级消息,而不是其他内容。我们已经在下面的字段旁边进行了验证。使用@ Leniel-Macaferi解决方案解决这个问题,这是我使用jQuery验证的方法:(添加了style =“display:none;”)

<div class="@(Html.ViewData.ModelState.IsValid ? "validation-summary-valid" : "validation-summary-errors")"
     data-valmsg-summary="true">
    <div>
        There are still some fields not filled in before we can submit this. Please correct.
    </div>
    <div style="display: none;">
        <ul>
            @foreach (var modelError in Model.SelectMany(keyValuePair => keyValuePair.Value.Errors))
            {
                <li>@modelError.ErrorMessage</li>
            }
        </ul>
    </div>
</div>