1

I'm trying to show a link as part of the validation message for a field. I'm using data attributes with custom error messages to set it:

[Required(ErrorMessage = "Message <a href='#'>link</a>")]
public string Field{ get; set; }

But when it renders the tags are escaped and literally prints:

Message <a href='#'>link</a>

Is it possible to have the link as part of the validation message but render correctly?

In case anyone's interested, here's how I accomplished it

public static MvcHtmlString ValidationHTMLMessageFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
{
    return ValidationHTMLMessageFor(helper, expression, (object)null);
}
public static MvcHtmlString ValidationHTMLMessageFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
{
    return ValidationHTMLMessageFor(helper, expression, new RouteValueDictionary(htmlAttributes));
}
public static MvcHtmlString ValidationHTMLMessageFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes)
{
    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;
    }

    string errors = "";
    foreach (ModelError error in helper.ViewData.ModelState[name].Errors)
    {
        TagBuilder tag = new TagBuilder("span");
        tag.Attributes.Add("class", HtmlHelper.ValidationMessageCssClassName);
        tag.MergeAttributes(htmlAttributes);
        tag.Attributes.Add("data-valmsg-for", name);
        tag.Attributes.Add("data-valmsg-replace", "true");

        var text = tag.ToString(TagRenderMode.StartTag);
        text += error.ErrorMessage;
        text += tag.ToString(TagRenderMode.EndTag);
        errors += text;
    }

    return MvcHtmlString.Create(errors);

}

Thanks Darin for pointing me in the right direction. I also found this that I used as a template Customize Html.ValidationMessageFor doesn't work in client side.

I'm new to this, so if anyone has any suggestions, please post. Thanks!

5 Answers 5

3

The code above doesn't work with client-side validation since it doesn't produce the tags required for client-side validation.

Here's an improvement on it that does that:

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

  TagBuilder tag = new TagBuilder("span");
  tag.Attributes.Add("class", HtmlHelper.ValidationMessageCssClassName);
  tag.MergeAttributes(htmlAttributes);
  tag.Attributes.Add("data-valmsg-for", name);
  tag.Attributes.Add("data-valmsg-replace", "true");
  var returnTag = new StringBuilder(tag.ToString(TagRenderMode.StartTag));

  if (helper.ViewData.ModelState[name] != null &&
      helper.ViewData.ModelState[name].Errors != null &&
      helper.ViewData.ModelState[name].Errors.Count > 0)
  {
    foreach (ModelError error in helper.ViewData.ModelState[name].Errors)
    {
      returnTag.Append(error.ErrorMessage);
    }
  }
  returnTag.Append(tag.ToString(TagRenderMode.EndTag));
  return MvcHtmlString.Create(returnTag.ToString());
}

Thanks for the original post - it was very helpful!

Sign up to request clarification or add additional context in comments.

1 Comment

Just posted an answer based on your's
2

Yes, it is possible but not with the standard helpers (ValidationSummary and ValidationMessageFor). You will have to write a custom helper to render those messages if you want to achieve that. You may take a look at the following post for an example of how to write a custom ValidationSummary helper that doesn't HTML encode the error messages as the standard one.

Comments

1

I'm using MVC4 so I can't say for sure about MVC3.

The only way I was able to insert a <br> tag (in my case), was to start with Jerode's method, but I also had to put a marker in the message string and replace it in the razor. So the sent message was "Line 1[BR]Line2". The razor was @Html.Raw(Html.ValidationMessageFor(x => Model.MyProperty).ToString().Replace("[BR]", "&lt;br&gt;"))

Hope that helps.

Comments

0

Another approach would be to just put the validation item inside of the Html.Raw.

@Html.Raw(Html.ValidationMessageFor(x => Model.MyProperty))

This is not as nice as the approach as the suggestions but should accomplish what you are looking for.

Comments

0

Building on the answer from @shycohen:

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

        TagBuilder tag = new TagBuilder("span");

        tag.Attributes.Add("data-valmsg-for", name);
        tag.Attributes.Add("data-valmsg-replace", "true");

        if (htmlAttributes != null)
        {
            tag.MergeAttributes((IDictionary<string, object>)HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
        }

        tag.AddCssClass(HtmlHelper.ValidationMessageCssClassName);

        var returnTag = new StringBuilder(tag.ToString(TagRenderMode.StartTag));

        if (helper.ViewData.ModelState[name] != null &&
            helper.ViewData.ModelState[name].Errors != null &&
            helper.ViewData.ModelState[name].Errors.Count > 0)
        {
            foreach (ModelError error in helper.ViewData.ModelState[name].Errors)
            {
                returnTag.Append(error.ErrorMessage);
            }
        }

        returnTag.Append(tag.ToString(TagRenderMode.EndTag));

        return MvcHtmlString.Create(returnTag.ToString());
    }

The htmlAttributes parameter is now optional and passed as an anonymous object to closer match ValidationMessageFor.

The other issue was not being able to add extra classes via htmlAttributes, as TagBuilder.MergeAttributes doesn't merge attribute values. This is solved by using TagBuilder.AddCssClass after merging the attributes to set the validation class.

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.