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!
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!
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.
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]", "<br>"))
Hope that helps.
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.
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.