Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you access the model errors on the client side?

I don't like the built in Html.ValidationSummary helper because I want a single message displayed at the top of my form such as...

There are validation errors. The invalid fields have been highlighted.

The built in ValidationSummary either doesn't output anything at all or outputs a list of all the fields. I just want a succinct message.

I have a div at the top of my form styled the way I want it and then when the page loads I hook the following method onto the form...

function CheckFormForValidationErrors() 
{
    if (!$("form").valid()) 
    {
        $(".validation-notification").css("visibility", "visible");
    }
}

This works great when there are validation errors on the client. The problem is when the client side validation succeeds but the server side validation such as a login form. If the user enters a proper username and password the form validation succeeds but the login fails. On the server side I am adding a model error like so...

ModelState.AddModelError(string.Empty, "Login failed");

But I can't figure out how to get the message to display on the client. If you have a better idea to accomplish my goal then I'm willing to scrap my idea and change course. What I'm trying to do shouldn't be that hard.

like image 830
oliwa Avatar asked Feb 02 '12 23:02

oliwa


People also ask

How do you handle client-side errors?

You can handle errors in a client-side human service by using a stand-alone error event handler or you can catch errors at a particular step by attaching a boundary error event to an individual service. To throw specific errors and end the processing of the service at a specified step, you can use error end events.

What are client-side errors?

Client-side failures are handled in a way that is similar to server-side failures. Message Queuing can move a message to its destination queue if, for example, the message cannot be moved from client to server. In this case, the message is moved to the client-side dead letter queue.

How can you achieve client-side validation?

Scripting languages such as JavaScript and VBScript are used for client-side validation. In this kind of validation, all the user input validation is done in user's browser only.


2 Answers

When you're in your view, the ModelState is located in the ViewData

 ViewData.ModelState

And to get the error messages:

 foreach(string key in ViewData.ModelState.Keys)
 {
     foreach (var error in ViewData.ModelState[key].Errors)
     {
         string err = error.ErrorMessage;
     }
 }

and in Razor, maybe this will work (my Razor is rusty)

@if (!ViewData.ModelState.IsValid)
{
    <ul>
    @foreach (string key in ViewData.ModelState.Keys)
    {
        @foreach (var error in ViewData.ModelState[key].Errors)
        {
            <li>@error.ErrorMessage</li>
        }
    }
    </ul>
}
like image 124
Nick Bork Avatar answered Sep 25 '22 11:09

Nick Bork


You have a whole myriad of options. If just you want to display the message create by this:

ModelState.AddModelError(string.Empty, "Login failed");

You simply need this in your view:

@Html.ValidationMessage(String.Empty)

This will however wrap the message in a <span class="field-validation-error"></span> but the default styles can be easily overridden if you so desire.

However based on the use-case that you describe above, what it sounds like you need is simply a static(ish) legend that explains "There are validation errors. The invalid fields have been highlighted." whenever there are one or more validation failures on the screen.

So how about this:

namespace MvcApplication.Web.Extensions
{
    public static class HtmlHelperExtensions
    {
        private const string ValidationLegendHtml = 
            "<div class=\"validation-legend\">{0}</div>";
        private const string DefaultValidationLegend =
            "There are validation errors. The invalid fields have been highlighted";

        public static MvcHtmlString ValidationLegend(
            this HtmlHelper htmlHelper, string message = DefaultValidationLegend)
        {
            if(htmlHelper.ViewData.ModelState.IsValid)
            {
                return null;
            }
            return new MvcHtmlString(String.Format(ValidationLegendHtml, message));
        }
    }
}

Then in the view you simply need to insert:

@Html.ValidationLegend()    //Use the default legend message
@Html.ValidationLegend("Something went wrong!")    //Specific message

This will then display the legend if the form is invalid and be totally invisible when its valid!

You will also need to add the namespace of the new extension to your Views/web.config (don't confuse with the root web.config) like so:

<pages pageBaseType="System.Web.Mvc.WebViewPage">
  <namespaces>
    <add namespace="System.Web.Mvc" />
    <add namespace="System.Web.Mvc.Ajax" />
    <add namespace="System.Web.Mvc.Html" />
    <add namespace="System.Web.Routing" />

    <!-- Add Me! -->
    <add namespace="MvcApplication.Web.Extensions"/>
  </namespaces>
</pages>

Unfortunately this won't give you intellisense for the new extension method, if this is important you can alternatively (or additionally) add a using statement for the extension to the top of your view immediately after the @model declaration like so:

@model MvcApplication.Web.Models.LoginModel
@using MvcApplication.Web.Extensions

EDIT

If you want to support showing this client side as well change the extension to:

public static MvcHtmlString ValidationLegend(
    this HtmlHelper htmlHelper, string message = DefaultValidationLegend)
{
    var tagBuilder = new TagBuilder("div");
    tagBuilder.SetInnerText(message);
    tagBuilder.AddCssClass("validation-legend");

    if(htmlHelper.ViewData.ModelState.IsValid)
    {
        tagBuilder.AddCssClass("hide");
    }

    return new MvcHtmlString(tagBuilder.ToString());
}

Make sure you have a .hide { display: none; } CSS rule.

EDIT Again

You can leave it up to jquery.validate to show/hide the legend for you automatically by setting the default value of the errorContainer config setting to match the legend's CSS class. This can be done by declaring the script includes like so:

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")"
        type="text/javascript"></script>
<script type="text/javascript">
    jQuery.validator.setDefaults({ errorContainer: '.validation-legend' });
</script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"
        type="text/javascript"></script>

Note the setDefaults call that comes after the jquery.validate script tag but before the jquery.validate.unobtrusive script tag. This order is important. Also note that any validation error will show all .validation-legends, so if you have any screens with two different validation-legends you might need to get a bit more fancy.

like image 27
Dan Turner Avatar answered Sep 25 '22 11:09

Dan Turner