Question
I created a server side property level validation attribute. But instead of applying it to a single field, I applied it to a list. This allows you to check the model as a whole.
Now I need to know how to convert this to work using the unobtrusive client-side validation built into MVC 3.
My current code is below to illustrate my problem ...
Scenario
The main scenario was the ability to sum all quantity values for each row in a list grouped by GroupNo field. If the sum of any of the groups was more than 10, then an error should be displayed.
I was asked to answer in a previous article to do this server-side work using the validation attribute for a list ...
Model:
public class ItemDetails
{
public int SerialNo { get; set; }
public string Description { get; set; }
public int GroupNo { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
}
public class MyViewModel
{
[EnsureMaxGroupItems(10, ErrorMessage = "You cannot have more than 10 items in each group")]
public IList<ItemDetails> Items { get; set; }
}
and the validation attribute itself:
[AttributeUsage(AttributeTargets.Property)]
public class EnsureMaxGroupItemsAttribute : ValidationAttribute
{
public int MaxItems { get; private set; }
public EnsureMaxGroupItemsAttribute(int maxItems)
{
MaxItems = maxItems;
}
public override bool IsValid(object value)
{
var items = value as IEnumerable<ItemDetails>;
if (items == null)
{
return true;
}
return items
.GroupBy(x => x.GroupNo)
.Select(g => g.Sum(x => x.Quantity))
.All(quantity => quantity <= MaxItems);
}
}
, , :
public ActionResult ListItems()
{
var model = new MyViewModel
{
Items = ItemsRepository.GetItems()
};
return View(model);
}
[HttpPost]
public ActionResult ListItems(MyViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
...
}
:
@model MyViewModel
@Html.ValidationSummary()
@using (Html.BeginForm())
{
@Html.EditorFor(x => x.Items)
<button type="submit">Go go go</button>
}
- , Items, (~/Views/Shared/EditorTemplates/ItemDetails.cshtml):
@model ItemDetails
@Html.HiddenFor(x => x.SerialNo)
@Html.LabelFor(x => x.Description)
@Html.HiddenFor(x => x.GroupNo)
@Html.LabelFor(x => x.Price)
@Html.TextBoxFor(x => x.Quantity)
?
, MVC. , EnsureMaxGroupItemsAttribute .
IClientValidatable :
Public Function GetClientValidationRules(metadata As System.Web.Mvc.ModelMetadata, context As System.Web.Mvc.ControllerContext) As System.Collections.Generic.IEnumerable(Of System.Web.Mvc.ModelClientValidationRule) Implements System.Web.Mvc.IClientValidatable.GetClientValidationRules
Dim result = New List(Of ModelClientValidationRule)
Dim rule = New ModelClientValidationRule() With { _
.ErrorMessage = "You cannot have more than 10 items in each group", _
.ValidationType = "itemscheck"}
result.Add(rule)
Return result
End Function
: VB # , , , #. VB, #.
JS :
jQuery.validator.unobtrusive.adapters.addBool("itemscheck");
... ...
jQuery.validator.addMethod("itemscheck", function (value, element, params) {
return false;
});
, ?