I've been searching for a while now and haven't found a solution yet.
I'm an ASP.NET and MVC4/Razor2 newbie, so I'm mostly editing the default project.
Anyway, my issue is that I'm using Twitter Bootstrap and I need to add an error
CSS class on a div
if the under-laying field is not valid.
So far I have this:
<div class="control-group error">
@Html.LabelFor(m => m.Password, new { @class = "control-label" })
<div class="controls">
@Html.PasswordFor(m => m.Password)
@Html.ValidationMessageFor(m => m.Password, null, new { @class = "inline-help error" })
</div>
</div>
What should I do to make Razor insert the error
class in the first div
of my snippet when the field is invalid.
UPDATE: I found a way to do it, but it doesn't feel right in comparison to the rest of the code. Is there a better way or is it the way to do it?
<div class="control-group@((!ViewData.ModelState.IsValidField("Password")) ? " error" : "")">
Like this:
public static class HtmlHelperExtensions
{
public static MvcHtmlString AddClassIfPropertyInError<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
string errorClassName)
{
var expressionText = ExpressionHelper.GetExpressionText(expression);
var fullHtmlFieldName = htmlHelper.ViewContext.ViewData
.TemplateInfo.GetFullHtmlFieldName(expressionText);
var state = htmlHelper.ViewData.ModelState[fullHtmlFieldName];
if (state == null)
{
return MvcHtmlString.Empty;
}
if (state.Errors.Count == 0)
{
return MvcHtmlString.Empty;
}
return new MvcHtmlString(errorClassName);
}
}
Then in your view:
<div class="control-group @Html.AddClassIfPropertyInError(x => x.Email, "error")">
<label class="control-label">Email</label>
<div class="controls">
<input type="text" placeholder="Email" name="Email" />
<span class="help-inline">@Html.ValidationMessageFor(x => x.Email)</span>
</div>
</div>
In addition to what Loki said, I came across to a nice and tidy solution to work with ModelState class and retrieve specific information to implement each validation logic i desired:
Assuming you want to check validation for a class in your model, named "InnerClass", and field "Name" :
if (ModelState["InnerClass.Name"].Errors.Count > 0)
{
foreach (var error in ModelState["InnerClass.Name"].Errors)
{
message = error.ErrorMessage;
isValidFlag = false;
}
}
Instead of above condition, you can use this also :
ModelState.IsValidField("InnerClass.Name")
ModelState class has great ways to retrieve validation information, so by using mentioned ways you can get more from it with a little workaround.
Hope it helps you!
You could also just check to see if there is a validation message, quick and dirty...
<div class="control-group @(Html.ValidationMessageFor(m => m.Password) != null ? "error" : "")">
You need to query the IsValidField
method of the ModelStateDictionary
in the ViewData
property of the view.
For a model that is not part of a hierarchy, the answer in your update will work.
For a model that is part of a hierarchy, you need to specify the field name in relation to the entire model.
To get the field name of a model property, use the Html.NameFor(...)
API.
ViewData.ModelState.IsValidField( Html.NameFor(m => m.Password).ToString() )
Let's take this one step further and extend the HtmlHelper
with a utility method that will output text when a field is invalid
namespace System.Web.Mvc.Html
{
public static class GeneralExtensions
{
public static MvcHtmlString TextForInvalidField<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string text)
{
if(htmlHelper.ViewData.ModelState.IsValidField(htmlHelper.NameFor(expression).ToString()))
{
return MvcHtmlString.Empty;
}
return MvcHtmlString.Create(text);
}
}
}
The end result (from your original example) will look like:
<div class="control-group @Html.TextForInvalidField(m => m.Password, "error")">
@Html.LabelFor(m => m.Password, new { @class = "control-label" })
<div class="controls">
@Html.PasswordFor(m => m.Password)
@Html.ValidationMessageFor(m => m.Password, null, new { @class = "inline-help error" })
</div>
</div>