22

Is it possible to customize the Html.ValidationMessageFor method so that it produces different HTML?

I want to do something similar to:

<div class="field-error-box">
    <div class="top"></div>
    <div class="mid"><p>This field is required.</p></div>
</div>

7 Answers 7

28

I am not sure if it's possible to use paragraph instead of default span, as it may make impossible for validation plugin to place error messages. But for div -s, thats easy - you could write custom html helper.

Something along these lines (may need further testing/coding). You will need to include the namespace of this static extension method in your view, or put this into System.Web.Mvc.Html directly.

public static class Validator
{
    public static MvcHtmlString MyValidationMessageFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
    {
        TagBuilder containerDivBuilder = new TagBuilder("div");
        containerDivBuilder.AddCssClass("field-error-box");

        TagBuilder topDivBuilder = new TagBuilder("div");
        topDivBuilder.AddCssClass("top");

        TagBuilder midDivBuilder = new TagBuilder("div");
        midDivBuilder.AddCssClass("mid");
        midDivBuilder.InnerHtml = helper.ValidationMessageFor(expression).ToString();

        containerDivBuilder.InnerHtml += topDivBuilder.ToString(TagRenderMode.Normal);
        containerDivBuilder.InnerHtml += midDivBuilder.ToString(TagRenderMode.Normal);

        return MvcHtmlString.Create(containerDivBuilder.ToString(TagRenderMode.Normal));
    }
}

As you see, this uses default ValidationMessageFor method, to not interfere with validation-plugin error message processing.

And you use this simply, as default validation message helper

@Html.MyValidationMessageFor(model => model.SomeRequiredField)
Sign up to request clarification or add additional context in comments.

4 Comments

This is just what I was looking for. I don't yet understand the problem with the validation plugin, but I'll look into that. Thanks!
This worked well for me, but make sure to declare your static helper class in the System.Web.Mvc.Html namespace and it will work really well.
How do you actually change the html generated by the 'ValidationMessageFor' message. I'm not talking about wrapping it in another div. Is it possible?
@gyozokudor See my answer below. I created that because with this answer I am seeing: 'HtmlHelper<TModel>' does not contain a definition for 'ValidationMessageFor'
7

I used another way:

    public static MvcHtmlString DivValidationMessageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
    {
        return MvcHtmlString.Create(htmlHelper.ValidationMessageFor(expression).ToString().Replace("span", "div"));
    }

This way you can use the built in way, but replace the span with a div.

If you need any other overloads of the function, just duplicate as necessary.

1 Comment

I like this approach. Much easier than duplicating all the ValidationMessageHelper code for such a small change.
3

You can implement your own ValidationMessageFor helper to emit your desired output or use some javascript to add/modify the rendered HTML code but the custom ValidationMessageFor implementation is the cleaner approach IMHO.

To implement your own ValidationMessageFor helper take a look at the ValidationExtensions.ValidationMessageFor and ValidationMessageHelper methods in the ASP.NET MVC source code.

Implementation Hints

Since GetFormContextForClientValidation is internal you have to work around that implementation by duplicating the internal functionality in your code:

FormContext formContext = htmlHelper.ViewContext.ClientValidationEnabled ? htmlHelper.ViewContext.FormContext : null;

Some other methods are private in ValidationExtensions like GetUserErrorMessageOrDefault you would need to duplicate that code too. What you can do to avoid duplicating code is to let ValidationExtentensions.ValidationMessageFor render the validation message string that is wrapped in a span and afterwards change the rendered string according to your requirements. Keep in mind that "null" is returned in case no error was found and that you'll need the data- HTML attributes in case you have unobtrusive JavaScript enabled.

You can download the ASP.NET MVC 3 source code from here

Comments

2

The only need for change of the default tag generation was in my case, that spans behavior results in anoying margin setups.

I resolved this by using 'display: block'

Maybe this helps some people..

Comments

0

Maybe you can put that code

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

on top of the answered function, so that the div doesn't appear on the form load.

Comments

0

I created ValidationMessageAsStringFor which just returns the error message as string. It is basically a simplified version of ValidationMessageFor:

public static MvcHtmlString ValidationMessageAsStringFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
{
    var field = ExpressionHelper.GetExpressionText(expression);
    string modelName = helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(field);

    if (!helper.ViewData.ModelState.ContainsKey(modelName))
    {
        return null;
    }

    var modelState = helper.ViewData.ModelState[modelName];
    var modelErrors = (modelState == null) ? null : modelState.Errors;
    var modelError = ((modelErrors == null) || (modelErrors.Count == 0)) ? null : modelErrors.FirstOrDefault(m => !String.IsNullOrEmpty(m.ErrorMessage)) ?? modelErrors[0];

    if (modelError == null)
    {
        return null;
    }

    var errorMessage = GetUserErrorMessageOrDefault(helper.ViewContext.HttpContext, modelError, modelState);

    return MvcHtmlString.Create(errorMessage);
}

private static string GetUserErrorMessageOrDefault(HttpContextBase httpContext, ModelError error, ModelState modelState)
{
    if (!string.IsNullOrEmpty(error.ErrorMessage))
    {
        return error.ErrorMessage;
    }

    if (modelState == null)
    {
        return null;
    }

    return modelState.Value?.AttemptedValue;
}

With this in place and after importing the namespace containing the new helper, just create the HTML code you need:

<div class="field-error-box">
    <div class="top"></div>
    <div class="mid"><p>@Html.ValidationMessageAsStringFor(m => m.FieldName)</p></div>
</div>

Comments

-2

Yes, just use a metamodel for the field:

[MetadataType(typeof(YourMetaData))]
public partial class YOURCLASS
{
    [Bind(Exclude = "objID")]
    public class YourMetaData
    {
        [Required(AllowEmptyStrings = false, ErrorMessage = "Please enter a name")]
        public object Name { get; set; }
    }
}

Change your message at the ErrorMessage field :) Hope this help :)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.