In my View, I’m making an entire html table editable. Each field in the table has validation. Here is some sample code…
<tbody>
foreach (Item item in Model.Items)
{
<tr>
<td>
@Html.TextBoxFor(x => em.Value)
@Html.ValidationMessageFor(x => em.Value)
</td>
</tr>
}
</tbody>
This is creating two problems on the browser.
1) Since the name attribute for all the controls are the same, when one field is invalid, the error messages are displayed on all fields of the same name.
2) Probably related to the names as well, when validating the form, if the control in the first row is invalid, the form is invalid. But controls in all other rows will not invalidate the form (even though the error messages are displayed).
if (!$(form).valid()) {
return false;
}
Any thoughts would be helpful.
UPDATE: THE SOLUTION
Per bobek suggestion, I created custom extensions for TextBoxInLoopFor and ValidationMessageInLoop. Below is the solution:
public static MvcHtmlString TextBoxInLoopFor<TModel, IItem, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IItem item, object htmlAttributes)
{
var model = item as IInLoopForModel;
if (model == null)
return MvcHtmlString.Empty;
var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
var fieldName = ExpressionHelper.GetExpressionText(expression);
var name = model.Name + fieldName;
var tag = new TagBuilder("input");
tag.MergeAttributes(new RouteValueDictionary(htmlAttributes));
tag.Attributes.Add("id", name);
tag.Attributes.Add("for", name);
tag.Attributes.Add("name", name);
tag.Attributes.Add("value", metadata.Model.ToString());
// Add the validation attributes
ModelState modelState;
if (html.ViewData.ModelState.TryGetValue(name, out modelState))
{
if (modelState.Errors.Count > 0)
tag.AddCssClass(HtmlHelper.ValidationInputCssClassName);
}
tag.MergeAttributes(html.GetUnobtrusiveValidationAttributes(name, metadata));
return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
}
public static MvcHtmlString ValidationMessageInLoopFor<TModel, IItem, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IItem item)
{
var model = item as IInLoopForModel;
if (model == null)
return MvcHtmlString.Empty;
var fieldName = ExpressionHelper.GetExpressionText(expression);
var name = model.Name + fieldName;
var tag = new TagBuilder("span");
tag.Attributes.Add("class", "field-validation-valid");
tag.Attributes.Add("data-valmsg-replace", "true");
tag.Attributes.Add("data-valmsg-for", name);
return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
}
This is not valid at all. MVC3 helpers for input are adding an ID for each one so here you will have multiple textboxes with same ID – it’s not valid in HTML.
You should manually create a TextBoxFor and ValidationFor your each property on the model, or if it’s possible try: