Проверка MVC3 – требуется от группы

Учитывая следующую модель обзора:

public class SomeViewModel { public bool IsA { get; set; } public bool IsB { get; set; } public bool IsC { get; set; } //... other properties } 

Я хочу создать пользовательский атрибут, который проверяет хотя бы одно из доступных свойств, это true. Я предполагаю, что могу прикрепить атрибут к свойству и назначить имя группы следующим образом:

 public class SomeViewModel { [RequireAtLeastOneOfGroup("Group1")] public bool IsA { get; set; } [RequireAtLeastOneOfGroup("Group1")] public bool IsB { get; set; } [RequireAtLeastOneOfGroup("Group1")] public bool IsC { get; set; } //... other properties [RequireAtLeastOneOfGroup("Group2")] public bool IsY { get; set; } [RequireAtLeastOneOfGroup("Group2")] public bool IsZ { get; set; } } 

Я хотел бы проверить на стороне клиента до отправки формы как значения в изменении формы, поэтому я предпочитаю избегать атрибута уровня classа, если это возможно.

Это потребует проверки как на стороне сервера, так и на стороне клиента для поиска всех свойств, имеющих идентичные значения имени группы, переданные в качестве параметра для настраиваемого атрибута. Это возможно? Любое руководство очень ценится.

    Вот один из способов для продолжения (есть другие способы, я просто иллюстрирую тот, который будет соответствовать вашей модели представления, как есть):

     [AttributeUsage(AttributeTargets.Property)] public class RequireAtLeastOneOfGroupAttribute: ValidationAttribute, IClientValidatable { public RequireAtLeastOneOfGroupAttribute(string groupName) { ErrorMessage = string.Format("You must select at least one value from group \"{0}\"", groupName); GroupName = groupName; } public string GroupName { get; private set; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { foreach (var property in GetGroupProperties(validationContext.ObjectType)) { var propertyValue = (bool)property.GetValue(validationContext.ObjectInstance, null); if (propertyValue) { // at least one property is true in this group => the model is valid return null; } } return new ValidationResult(FormatErrorMessage(validationContext.DisplayName)); } private IEnumerable GetGroupProperties(Type type) { return from property in type.GetProperties() where property.PropertyType == typeof(bool) let attributes = property.GetCustomAttributes(typeof(RequireAtLeastOneOfGroupAttribute), false).OfType() where attributes.Count() > 0 from attribute in attributes where attribute.GroupName == GroupName select property; } public IEnumerable GetClientValidationRules(ModelMetadata metadata, ControllerContext context) { var groupProperties = GetGroupProperties(metadata.ContainerType).Select(p => p.Name); var rule = new ModelClientValidationRule { ErrorMessage = this.ErrorMessage }; rule.ValidationType = string.Format("group", GroupName.ToLower()); rule.ValidationParameters["propertynames"] = string.Join(",", groupProperties); yield return rule; } } 

    Теперь давайте определим controller:

     public class HomeController : Controller { public ActionResult Index() { var model = new SomeViewModel(); return View(model); } [HttpPost] public ActionResult Index(SomeViewModel model) { return View(model); } } 

    и вид:

     @model SomeViewModel   @using (Html.BeginForm()) { @Html.EditorFor(x => x.IsA) @Html.ValidationMessageFor(x => x.IsA) 
    @Html.EditorFor(x => x.IsB)
    @Html.EditorFor(x => x.IsC)
    @Html.EditorFor(x => x.IsY) @Html.ValidationMessageFor(x => x.IsY)
    @Html.EditorFor(x => x.IsZ)
    }

    Последняя оставшаяся часть будет заключаться в регистрации адаптеров для проверки на стороне клиента:

     jQuery.validator.unobtrusive.adapters.add( 'group', [ 'propertynames' ], function (options) { options.rules['group'] = options.params; options.messages['group'] = options.message; } ); jQuery.validator.addMethod('group', function (value, element, params) { var properties = params.propertynames.split(','); var isValid = false; for (var i = 0; i < properties.length; i++) { var property = properties[i]; if ($('#' + property).is(':checked')) { isValid = true; break; } } return isValid; }, ''); 

    Исходя из ваших конкретных требований, код может быть адаптирован.

    Использование require_from_group из команды проверки jquery:

    Проект jQuery-validation имеет подпапку в папке src, называемую дополнительной . Вы можете проверить это здесь .

    В этой папке у нас есть много дополнительных методов проверки, которые не являются общими, поэтому они не добавляются по умолчанию.

    Как вы видите в этой папке, существует так много методов, которые вам нужно выбрать, выбрав тот метод проверки, который вам действительно нужен.

    Основываясь на вашем вопросе, требуемый метод валидации называется require_from_group из дополнительной папки. Просто загрузите этот связанный файл, который находится здесь, и поместите его в папку приложения Scripts .

    Документация этого метода объясняет это:

    Позволяет вам сказать: «по крайней мере X входов, которые соответствуют селектору Y, должны быть заполнены».

    Конечным результатом является то, что ни один из этих входов:

    … будет проверяться, если по крайней мере один из них не будет заполнен.

    partnumber: {require_from_group: [1, “. productinfo”]}, описание: {require_from_group: [1, “. productinfo”]}

    options [0]: количество полей, которые должны быть заполнены в параметрах группы 2 : селектор CSS, который определяет группу условно обязательных полей

    Почему вам нужно выбрать эту реализацию:

    Этот метод проверки является общим и работает для каждого input (текст, флажок, радио и т. Д.), textarea и select . Этот метод также позволяет указать минимальное количество необходимых входных данных, которые необходимо заполнить, например

     partnumber: {require_from_group: [2,".productinfo"]}, category: {require_from_group: [2,".productinfo"]}, description: {require_from_group: [2,".productinfo"]} 

    Я создал два classа RequireFromGroupAttribute и RequireFromGroupFieldAttribute , которые помогут вам как на стороне сервера, так и на стороне клиента.

    RequireFromGroupAttribute classа RequireFromGroupAttribute

    RequireFromGroupAttribute происходит только от Attribute . Класс используется только для конфигурации, например, для установки количества полей, которые необходимо заполнить для проверки. Вы должны предоставить этому classу class селектора CSS, который будет использоваться методом проверки, чтобы получить все элементы в одной группе. Поскольку по умолчанию число обязательных полей равно 1, этот атрибут используется только для украшения вашей модели, если минимальное требование в группе spcefied больше, чем номер по умолчанию.

     [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] public class RequireFromGroupAttribute : Attribute { public const short DefaultNumber = 1; public string Selector { get; set; } public short Number { get; set; } public RequireFromGroupAttribute(string selector) { this.Selector = selector; this.Number = DefaultNumber; } public static short GetNumberOfRequiredFields(Type type, string selector) { var requiredFromGroupAttribute = type.GetCustomAttributes().SingleOrDefault(a => a.Selector == selector); return requiredFromGroupAttribute?.Number ?? DefaultNumber; } } 

    RequireFromGroupFieldAttribute classа RequireFromGroupFieldAttribute

    RequireFromGroupFieldAttribute который происходит из ValidationAttribute и реализует IClientValidatable . Вы должны использовать этот class для каждого свойства вашей модели, которое участвует в вашей проверке группы. Вы должны пройти class селектора css.

     [AttributeUsage(AttributeTargets.Property)] public class RequireFromGroupFieldAttribute : ValidationAttribute, IClientValidatable { public string Selector { get; } public bool IncludeOthersFieldName { get; set; } public RequireFromGroupFieldAttribute(string selector) : base("Please fill at least {0} of these fields") { this.Selector = selector; this.IncludeOthersFieldName = true; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { var properties = this.GetInvolvedProperties(validationContext.ObjectType); ; var numberOfRequiredFields = RequireFromGroupAttribute.GetNumberOfRequiredFields(validationContext.ObjectType, this.Selector); var values = new List { value }; var otherPropertiesValues = properties.Where(p => p.Key.Name != validationContext.MemberName) .Select(p => p.Key.GetValue(validationContext.ObjectInstance)); values.AddRange(otherPropertiesValues); if (values.Count(s => !string.IsNullOrWhiteSpace(Convert.ToString(s))) >= numberOfRequiredFields) { return ValidationResult.Success; } return new ValidationResult(this.GetErrorMessage(numberOfRequiredFields, properties.Values), new List { validationContext.MemberName }); } public IEnumerable GetClientValidationRules(ModelMetadata metadata, ControllerContext context) { var properties = this.GetInvolvedProperties(metadata.ContainerType); var numberOfRequiredFields = RequireFromGroupAttribute.GetNumberOfRequiredFields(metadata.ContainerType, this.Selector); var rule = new ModelClientValidationRule { ValidationType = "requirefromgroup", ErrorMessage = this.GetErrorMessage(numberOfRequiredFields, properties.Values) }; rule.ValidationParameters.Add("number", numberOfRequiredFields); rule.ValidationParameters.Add("selector", this.Selector); yield return rule; } private Dictionary GetInvolvedProperties(Type type) { return type.GetProperties() .Where(p => p.IsDefined(typeof(RequireFromGroupFieldAttribute)) && p.GetCustomAttribute().Selector == this.Selector) .ToDictionary(p => p, p => p.IsDefined(typeof(DisplayAttribute)) ? p.GetCustomAttribute().Name : p.Name); } private string GetErrorMessage(int numberOfRequiredFields, IEnumerable properties) { var errorMessage = string.Format(this.ErrorMessageString, numberOfRequiredFields); if (this.IncludeOthersFieldName) { errorMessage += ": " + string.Join(", ", properties); } return errorMessage; } } 

    Как использовать его в своей модели просмотра?

    В вашей модели вот как ее использовать:

     public class SomeViewModel { internal const string GroupOne = "Group1"; internal const string GroupTwo = "Group2"; [RequireFromGroupField(GroupOne)] public bool IsA { get; set; } [RequireFromGroupField(GroupOne)] public bool IsB { get; set; } [RequireFromGroupField(GroupOne)] public bool IsC { get; set; } //... other properties [RequireFromGroupField(GroupTwo)] public bool IsY { get; set; } [RequireFromGroupField(GroupTwo)] public bool IsZ { get; set; } } 

    По умолчанию вам не нужно украшать вашу модель RequireFromGroupAttribute потому что число по умолчанию для обязательных полей по умолчанию равно 1. Но если вы хотите, чтобы число обязательных полей было отличным от 1, вы можете сделать следующее:

     [RequireFromGroup(GroupOne, Number = 2)] public class SomeViewModel { //... } 

    Как использовать его в коде просмотра?

     @model SomeViewModel    @using (Html.BeginForm()) { @Html.CheckBoxFor(x => x.IsA, new { @class="Group1"})A @Html.ValidationMessageFor(x => x.IsA) 
    @Html.CheckBoxFor(x => x.IsB, new { @class = "Group1" }) B
    @Html.CheckBoxFor(x => x.IsC, new { @class = "Group1" }) C
    @Html.CheckBoxFor(x => x.IsY, new { @class = "Group2" }) Y @Html.ValidationMessageFor(x => x.IsY)
    @Html.CheckBoxFor(x => x.IsZ, new { @class = "Group2" })Z
    }

    Обратите внимание, что RequireFromGroupField вами групповой селектор при использовании атрибута RequireFromGroupField используется в вашем представлении, указывая его как class на каждом входе, участвующем в ваших группах.

    Это все для проверки на стороне сервера.

    Давайте поговорим о проверке на стороне клиента.

    Если вы проверите реализацию RequireFromGroupFieldAttribute classе RequireFromGroupFieldAttribute вы увидите, что я использую строку requirefromgroup а не require_from_group как имя метода для свойства ValidationType . Это связано с тем, что ASP.Net MVC разрешает только имя типа проверки содержать буквенно-цифровой символ и не должно начинаться с числа. Поэтому вам нужно добавить следующий javascript:

     $.validator.unobtrusive.adapters.add("requirefromgroup", ["number", "selector"], function (options) { options.rules["require_from_group"] = [options.params.number, options.params.selector]; options.messages["require_from_group"] = options.message; }); 

    Часть javascript очень проста, поскольку в реализации функции адаптера мы просто делегируем проверку правильному методу require_from_group .

    Поскольку он работает с каждым типом input , textarea и элементами select , я могу думать, что этот способ более общий.

    Надеюсь, это поможет!

    Я внедрил внушительный ответ Дарина в мое приложение, за исключением того, что я добавил его для строк, а не для булевых значений. Это было для таких вещей, как имя / компания, или телефон / электронная почта. Я любил его, кроме одной маленькой нитпика.

    Я пытался представить свою форму без рабочего телефона, мобильного телефона, домашнего телефона или электронной почты. У меня есть четыре отдельные ошибки проверки на стороне клиента. Это хорошо для меня, потому что он позволяет пользователям точно знать, какие поля могут быть заполнены, чтобы ошибка исчезла.

    Я набрал адрес электронной почты. Теперь единственная проверка по электронной почте ушла, но трое остались под номерами телефонов. Они больше не являются ошибками.

    Итак, я переназначил метод jQuery, который проверяет валидацию для учетной записи для этого. Код ниже. Надеюсь, это поможет кому-то.

     jQuery.validator.prototype.check = function (element) { var elements = []; elements.push(element); var names; while (elements.length > 0) { element = elements.pop(); element = this.validationTargetFor(this.clean(element)); var rules = $(element).rules(); if ((rules.group) && (rules.group.propertynames) && (!names)) { names = rules.group.propertynames.split(","); names.splice($.inArray(element.name, names), 1); var name; while (name = names.pop()) { elements.push($("#" + name)); } } var dependencyMismatch = false; var val = this.elementValue(element); var result; for (var method in rules) { var rule = { method: method, parameters: rules[method] }; try { result = $.validator.methods[method].call(this, val, element, rule.parameters); // if a method indicates that the field is optional and therefore valid, // don't mark it as valid when there are no other rules if (result === "dependency-mismatch") { dependencyMismatch = true; continue; } dependencyMismatch = false; if (result === "pending") { this.toHide = this.toHide.not(this.errorsFor(element)); return; } if (!result) { this.formatAndAdd(element, rule); return false; } } catch (e) { if (this.settings.debug && window.console) { console.log("Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method.", e); } throw e; } } if (dependencyMismatch) { return; } if (this.objectLength(rules)) { this.successList.push(element); } } return true; }; 

    Я знаю, что это старая нить, но я просто столкнулся с одним и тем же сценарием и нашел несколько решений и увидел тот, который решает вопрос Мэтта выше, поэтому я подумал, что поделюсь тем, кто сталкивается с этим ответом. Проверьте: MVC3 ненавязчивая группа проверки входных данных

    Interesting Posts

    Testdisk / Photorec для возврата данных из нераспределенного раздела

    Как связать кнопку WPF с командой в ViewModelBase?

    преобразовать формат столбца data.frame из символа в фактор

    OS X: Извлечь аудиосистему системы, чтобы удалить высокий уровень шума?

    Точка с запятой после имени метода в файле реализации Objective-C

    Я не могу увидеть элементы FirebaseRecyclerAdapter на моем макете

    .net динамически обновлять app.config

    В форме Django, как сделать поле readonly (или отключено), чтобы он не редактировался?

    Что именно будет скрывать SSID моего маршрутизатора, и могут ли все мои устройства подключиться к нему?

    Как настроить автозаполнение jquery-ui в Rails

    Развертывание проекта Maven throws java.util.zip.ZipException: недопустимый заголовок LOC (плохая подпись)

    Как был написан первый компилятор?

    Android ACTION_IMAGE_CAPTURE Intent

    Программно создать веб-сайт в IIS с помощью C # и установить номер порта

    понять цель jsf ui: состав

    Давайте будем гением компьютера.