Customize Html.ValidationMessageFor doesn't wo

2019-08-01 02:18发布

问题:

I am working on mvc3 application, i have created a custom validationMessage to validate a simple field. This custom validationMessage only show an div with title and message instead of the default span, i have this :

@Html.ValidationMessageCustomFor(model => model.FirstName)

This is the code to my custom validation:

public static MvcHtmlString ValidationCustomFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
{
    string propertyName = ExpressionHelper.GetExpressionText(expression);
    string name = helper.AttributeEncode(helper.ViewData.TemplateInfo.GetFullHtmlFieldName(propertyName));

    if (helper.ViewData.ModelState[name] == null ||
        helper.ViewData.ModelState[name].Errors == null ||
        helper.ViewData.ModelState[name].Errors.Count == 0)
    {
        return MvcHtmlString.Empty;
    }

    StringBuilder htmlValidation = new StringBuilder();

    htmlValidation.AppendLine("<div>");
    htmlValidation.AppendLine("<table>");
    htmlValidation.AppendLine("<tr>");
    htmlValidation.AppendLine("<td><img src='/Content/Imgages/Error.jpg'></td>");
    htmlValidation.AppendLine("</tr>");
    htmlValidation.AppendLine("<tr>");
    htmlValidation.AppendFormat("<td>{0}</td>", name);
    htmlValidation.AppendLine("</tr>");
    htmlValidation.AppendLine("<tr>");
    htmlValidation.AppendFormat("<td>{0}</td>", helper.ViewData.ModelState[name].Errors[0].ErrorMessage);
    htmlValidation.AppendLine("</tr>");
    htmlValidation.AppendLine("</table>");
    htmlValidation.AppendLine("</div>");

    return MvcHtmlString.Create(htmlValidation.ToString());

}

This works fine when I use server side validation, but when I active client side validation it doesn't work.

The default Html.ValidationMessageFor yes works fine with client side validation, but my custom Html.ValidationMessageCustomFor doesn't work.

Could you please help me, any ideas?


I modified my custom helper, I added the "data-valmsg-for" atrib for the span, it is the code :

 public static MvcHtmlString ValidationCustomFor2<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
    {
        string propertyName = ExpressionHelper.GetExpressionText(expression);
        string name = helper.AttributeEncode(helper.ViewData.TemplateInfo.GetFullHtmlFieldName(propertyName));

        if (helper.ViewData.ModelState[name] == null ||
            helper.ViewData.ModelState[name].Errors == null ||
            helper.ViewData.ModelState[name].Errors.Count == 0)
        {
            return MvcHtmlString.Empty;
        }

        TagBuilder tag = new TagBuilder("span");
        tag.Attributes.Add("class", "field-validation-error");
        tag.Attributes.Add("data-valmsg-for", name);
        tag.Attributes.Add("data-valmsg-replace", "true");

        var text = tag.ToString(TagRenderMode.StartTag);
        text += "<b>My custom html</b>";
        text += tag.ToString(TagRenderMode.EndTag);


        return MvcHtmlString.Create(text);

    }

But it doesn't appear, it looks I should do something from the client side, but I don't know WHAT? Any ideas please?

回答1:

The ASP.NET MVC3 validation works using both server-side and client-side methods.

When you write server-side code to add validation messages to the rendered page (like in your helper method), the code is only invoked when the request is actually sent to the server, while the client-side validation, when enabled, prevents the request from being sent back to the server.

Default implementation of MVC3 client-side validation works using a technique called Unobtrusive JavaScript validation, by adding some data- attributes to the page, and then finding and manipulating the marked elements in the JavaScript.

For understanding the Unobtrusive JavaScript validation, try searching for it, there's a ton of resources out there. For example:

http://www.codeproject.com/KB/aspnet/CustomClientSideValidatio.aspx

In your case, when you want to specify where to display the validation message, you should mark the HTML element with a data-valmsg-for attribute. For example:

<SPAN data-valmsg-for="id_of_the_input_to_validate"></span>

The validation JavaScript will look for the above attribute and fill the SPAN with the validation message in case the input is not valid. Again, this happens before the postback of the form, so your server-side code that shows the validation message is never called, because the validation prevents the form from submitting.

Here's a code snippet from the actual validation message helper included in the MVC3:

TagBuilder builder = new TagBuilder("span"); 
...
if (htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled) {
    builder.MergeAttribute("data-valmsg-for", modelName); 
    builder.MergeAttribute("data-valmsg-replace", replaceValidationMessageContents.ToString().ToLowerInvariant());
} 

Edit: Reply to your edited question:

The first time the page is rendered, your first if is true (there is no validation error messages to render), thus you return an empty string. Your page will not have any of the data- attributes in it.

You should render the <span data-valmsg-for="..."></span> all the time, even when you don't have any error messages, so that the JavaScript library knows where to put the error in case it happens on the client side.