Моя модель представления
Public Class ViewModel
<SelectOne()>
Public Property Collection As List(Of Item)
End Class
Моя модель
Public Class Item
<SelectOneProperty(TargetValue:=True, ErrorMessage:="Select at least one.")>
Public Property Selected As Boolean
Public Property Value As String
End Class
На мой взгляд, я визуализирую ViewModel.Collection
с помощью шаблона редактора.
@Html.CheckBoxFor(Function(item) item.Selected)
@Html.HiddenFor(Function(item) item.Value)
Теперь я хочу убедиться, что хотя бы один флажок отмечен с помощью проверки на стороне клиента.
Я могу добиться этого, установив собственный атрибут проверки для свойства Item.Selected
и зарегистрировав новый адаптер через $.validator.unobtrusive.adapters.add()
.
Но я чувствую, что атрибут скорее должен быть в свойстве ViewModel.Collection
, так как на стороне сервера я уже проверяю, имеет ли один из элементов коллекции Selected = True
, используя эту пользовательскую проверку:
<AttributeUsage(AttributeTargets.Field Or AttributeTargets.Property, AllowMultiple:=False, Inherited:=False)>
Public Class SelectOneAttribute
Inherits ValidationAttribute
Protected Overrides Function IsValid(value As Object, validationContext As ValidationContext) As ValidationResult
Dim list As IList
If value Is Nothing Then
Return Nothing
End If
If TypeOf value Is IEnumerable Then
list = CType(value, IList)
Else
list = New Object() {value}
End If
Dim count As Integer = (From item In list
From prop In item.GetType().GetProperties()
Let attributes = prop.GetCustomAttributes(GetType(RequireOneOrMoreIndicatorAttribute), False)
Where attributes.Count > 0
From attribute In attributes
Where attribute.TargetValue = prop.GetValue(item, Nothing)).Count()
If count > 0 Then
Return Nothing
End If
Return New ValidationResult(FormatErrorMessage(validationContext.DisplayName))
End Function
End Class
Он использует отражение SelectOnePropertyAttribute
, чтобы найти, какое свойство проверять:
<AttributeUsage(AttributeTargets.Field Or AttributeTargets.Property, AllowMultiple:=False, Inherited:=False)>
Public Class SelectOnePropertyAttribute
Inherits ValidationAttribute
Implements IClientValidatable
Public Property TargetValue As Object
Public Sub New(targetValue As Object)
Me.TargetValue = targetValue
End Sub
Public Overrides Function IsValid(value As Object) As Boolean
Return True
End Function
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 rule As New ModelClientValidationRule With {
.ValidationType = "selectone",
.ErrorMessage = Me.ErrorMessage
}
Return New ModelClientValidationRule() {rule}
End Function
End Class
И это проверка на стороне клиента
$.validator.unobtrusive.adapters.add("selectone", function (options) {
options.rules["selectone"] = {};
options.messages["selectone"] = options.message;
});
$.validator.addMethod("selectone", function (value, element, parameters) {
var $el = $(element),
name = $el.attr("name"),
field = name.replace(/\[.*$/, "").replace(".", "_"),
attr = name.replace(/^.*\./, ""),
test = new RegExp(field + "\\[\\d\\]\." + attr);
var inputs = $("input[id^=" + field + "]:not([disabled]):not([type=hidden])").filter("input[name$=" + attr + "]");
for(var i = 0; i < this.errorList.length; i++) {
var name = $(this.errorList[i].element).attr("name");
// Do not write out the error more than once.
if (test.test(name)) return true;
}
return inputs.length == 0 || inputs.filter(":checked:not([disabled])").val();
});