Change all metadata property error messages for StringLengthAttribute - asp.net-mvc

Can I modify all properties StringLengthAttribute error message of a modelmetadata ? I can change one by one by calling GetValidators().
Here is my htmlhelper code:
public static HtmlString TestHelper<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> pFieldExpression)
{
ModelMetadata fieldmetadata = ModelMetadata.FromLambdaExpression(pFieldExpression, html.ViewData);
// option 1
var a = (from s in fieldmetadata.ModelType.GetProperties()
select s.GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.StringLengthAttribute), true)
);
foreach (var i in a)
{
// ther is no error message
}
// option 2
var validatorsDictionary = ModelValidatorProviders.Providers
.GetValidators(fieldmetadata, html.ViewContext)
.SelectMany(v => v.GetClientValidationRules());
// validatorsDictionary is empty and seems cannot modify metadata
return new HtmlString("test");
}
Thanks in advance

Related

Rendering special characters in readonly input field

In ASP.NET MVC application i'm using TagBuilder to create readonly input field.
public class DisplayTextFieldBuilder<TModel, TProperty>
{
private HtmlHelper<TModel> _helper;
private Expression<Func<TModel, TProperty>> _expression;
public DisplayTextFieldBuilder(HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
{
_helper = helper;
_expression = expression;
}
public override string ToString()
{
var id = _helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(ExpressionHelper.GetExpressionText(_expression));
var name = _helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(ExpressionHelper.GetExpressionText(_expression));
var value = HttpUtility.HtmlEncode(_helper.DisplayFor(_expression).ToString());
TagBuilder builder = new TagBuilder("input");
builder.MergeAttribute("value", value);
builder.MergeAttribute("id", id);
builder.MergeAttribute("name", name);
builder.MergeAttribute("readonly", "readonly");
var html = builder.ToString(TagRenderMode.SelfClosing);
return html;
}
However certain string does not render properly. For example, lets say the model property value is Foe`s BBQ (note it has apostrophe)
then
HttpUtility.HtmlEncode(_helper.DisplayFor(_expression).ToString())
renders this value as Foe&#39;s, BBQ
if i remove html encoding then
_helper.DisplayFor(_expression).ToString()
renders this value as Foe's, BBQ
what i am missing?

Access ModelState from within my custom helper?

I just want to make a such Helper:
public static MvcHtmlString HasError<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
{
StringBuilder sb = new StringBuilder();
if (!ModelState.IsValidField(ExpressionHelper.GetExpressionText(expression)))
sb.Append("has-error");
return MvcHtmlString.Create(sb.ToString());
}
So the question - how to access to the actual ModelState here?
You can use the following code:
foreach (var state in htmlHelper.ViewData.ModelState)
{
// Do what you what with the ModelState here
foreach (var error in state.Value.Errors)
{
// Display error here
}
}

MVC3 Extension for ValidatorMessage

i have wrote a extension method for customize my validation messages, like this:
namespace Helpers
{
public static class HtmlHelpers
{
public static MvcHtmlString ValidationMessageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
{
var sb = new StringBuilder();
string modelName = ExpressionHelper.GetExpressionText(expression);
ModelState state = htmlHelper.ViewData.ModelState[modelName];
if (state != null)
if ((state.Errors != null) && (state.Errors.Count > 0))
{
sb.Append("<div class='error-left'></div>");
sb.Append("<div class='error-inner'>");
sb.Append(htmlHelper.ValidationMessageFor(expression).ToString());
sb.Append("</div>");
}
return MvcHtmlString.Create(sb.ToString());
}
}
}
So in my view, i put
#using HtmlHelpers
and also
#Html.ValidationMessageFor(model => model.Name)
But i'm getting this exception:
The call is ambiguous between the following methods or properties: 'ContinentalWeb.Helpers.HtmlHelpers.ValidationMessageFor<ContinentalWeb.Models.Maker,string>(System.Web.Mvc.HtmlHelper<Type>, System.Linq.Expressions.Expression<System.Func<Type,string>>)' and 'System.Web.Mvc.Html.ValidationExtensions.ValidationMessageFor<Type,string>(System.Web.Mvc.HtmlHelper<Type>, System.Linq.Expressions.Expression<System.Func<Type,string>>)'
I'm new in MVC... any help?
Thank you!
Your extension method has the same name and signature as the default one. This is not possible because you cannot have the 2 at the same time in scope. You will have to give a different name to your method or change the number and/or type of arguments to avoid the conflict.
For example:
public static MvcHtmlString CustomValidationMessageFor<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression
)
{
...
}
and then use it like this:
#Html.CustomValidationMessageFor(model => model.Name)

How do I prevent MVC3 html escaping my validation messages?

I'm trying to output a validation message in MVC3 which contains a link.
I'm outputting the error message place holder like this
#Html.ValidationMessageFor(model => model.Email)
The problem is, the error message is html escaped which is fine most times, but I'd like a link to be in the middle.
<span class="field-validation-error" data-valmsg-for="Email" data-valmsg-replace="true">This e-mail address is already registed. <a href="%url_token%">Click here to reset.</a></span>
How do I prevent this from happening?
This works but is not a solution, rather, a temporary work around.
#{
string s = Html.ValidationMessageFor(model => model.Email).ToString();
}
#Html.Raw(HttpUtility.HtmlDecode(s))
Looking at the code with reflector it doesn't seem to have a method for that, the more complete overload is:
public static MvcHtmlString ValidationMessageFor<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
string validationMessage,
object htmlAttributes)
{
return htmlHelper.ValidationMessageFor<TModel, TProperty>(expression, validationMessage, ((IDictionary<string, object>) HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)));
}
What you can do however is to create an extension method that returns the string as you want, like this:
public static class ValidationExtender
{
public static IHtmlString HtmlValidationMessageFor<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression)
{
return MvcHtmlString.Create(htmlHelper.ValidationMessageFor(expression).ToHtmlString());
}
}
You can use a MvcHtmlString. See MvcHtmlString.Create(). It will output the html without escaping.
Taking #BrunoLM's lead, The following extension methods should give you what you need. I've only done basic testing on this, but it does work.
public static class HtmlHelperExtensions
{
private static readonly string htmlErrorPlaceholder = "##__html#Error#Placeholder__##";
public static IHtmlString HtmlValidationMessageFor<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
object htmlAttributes)
{
var name = expression.GetMemberName();
var isInError = htmlHelper.ViewContext.ViewData.ModelState.ContainsKey(name);
var message = htmlHelper.ValidationMessageFor(expression, htmlErrorPlaceholder, htmlAttributes);
if (isInError && !MvcHtmlString.IsNullOrEmpty(message))
{
var realError = htmlHelper.ViewContext.ViewData.ModelState[name].Errors.First().ErrorMessage;
return htmlHelper.Raw(message.ToString().Replace(htmlErrorPlaceholder, realError));
}
return MvcHtmlString.Empty;
}
}
public static class Expression_1Extensions
{
public static string GetMemberName<TModel, TProperty>(this Expression<Func<TModel, TProperty>> expression)
{
switch (expression.Body.NodeType)
{
case ExpressionType.MemberAccess:
MemberExpression memberExpression = (MemberExpression)expression.Body;
return memberExpression.Member is PropertyInfo ? memberExpression.Member.Name : null;
}
throw new NotSupportedException();
}
}

Can I add a CSS class definition to the Html.LabelFor in MVC3?

I hope someone out there has some ideas. I would like to tidy up my code and so I already used the Html.LabelFor. However now I want to assign a CSS class to the labels.
Html.LabelFor(model => model.Customer.Description ????)
Does anyone out there know if this is possible in MVC3. Note it's MVC3 I am using. Already I saw a post that talked about MVC2 and there being no simple solution.
Here you go buddy-o:
namespace System.Web.Mvc.Html
{
using System;
using Collections.Generic;
using Linq;
using Linq.Expressions;
using Mvc;
public static class LabelExtensions
{
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object htmlAttributes)
{
return html.LabelFor(expression, null, htmlAttributes);
}
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string labelText, object htmlAttributes)
{
return html.LabelHelper(
ModelMetadata.FromLambdaExpression(expression, html.ViewData),
ExpressionHelper.GetExpressionText(expression),
HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes),
labelText);
}
private static MvcHtmlString LabelHelper(this HtmlHelper html, ModelMetadata metadata, string htmlFieldName, IDictionary<string, object> htmlAttributes, string labelText = null)
{
var str = labelText
?? (metadata.DisplayName
?? (metadata.PropertyName
?? htmlFieldName.Split(new[] { '.' }).Last()));
if (string.IsNullOrEmpty(str))
return MvcHtmlString.Empty;
var tagBuilder = new TagBuilder("label");
tagBuilder.MergeAttributes(htmlAttributes);
tagBuilder.Attributes.Add("for", TagBuilder.CreateSanitizedId(html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName)));
tagBuilder.SetInnerText(str);
return tagBuilder.ToMvcHtmlString(TagRenderMode.Normal);
}
private static MvcHtmlString ToMvcHtmlString(this TagBuilder tagBuilder, TagRenderMode renderMode)
{
return new MvcHtmlString(tagBuilder.ToString(renderMode));
}
}
}
There is no built in way to do this in MVC 3. You will have to write your helper that does this. Take a look at the LabelExtensions class to see how it is done.

Resources