Подтвердить что ты не робот

Групповые сообщения проверки для нескольких свойств вместе в одно сообщение asp.net mvc

У меня есть модель представления, у которой есть свойства года/месяца/дня для кого-то даты рождения. Все эти поля необходимы. Прямо сейчас, если кто-то не вводит ничего на дату рождения, они получают 3 отдельных сообщения об ошибках.

Date of birth fields

То, что я хочу сделать, - это как-то объединить эти сообщения об ошибках в одно сообщение, которое просто говорит: "Требуется дата рождения". Поэтому, если 1 или более из этих полей пустые, они всегда будут просто получать 1 сообщение проверки.

Мне НУЖНО это работать с проверкой на стороне клиента с помощью проверки jquery и ненавязчивого подтверждения. Я знаю, что это возможно с помощью плагина jquery validate, посмотрев на это question. Но я не знаю, как добиться этого с помощью asp.net mvc, используя атрибуты проверки на моей модели и ненавязчивую проверку. Надеемся, что некоторые встроенные способы группировки свойств для целей валидации, но если это невозможно, с помощью специального атрибута проверки?

Вот что выглядит моя существующая модель и вид:

Модель:

public class MyModel {
    [Required(ErrorMessage = "Year is required")]
    public int Year { get; set; }
    [Required(ErrorMessage = "Month is required")]
    public int Month { get; set; }
    [Required(ErrorMessage = "Day is required")]
    public int Day { get; set; }
}

Вид:

<div>
    <label>Date of birth: <span style="color:red;">*</span></label>
    <div>@Html.DropDownListFor(m => m.Year, ApplicationModel.GetSelectListForDateRange(DateTime.Today.Year - 16, DateTime.Today.Year - 10), "", new{data_description="birthDate"})@Html.LabelFor(m => m.StudentBirthYear)</div>
    <div>@Html.DropDownListFor(m => m.Month, ApplicationModel.GetSelectListForDateRange(1, 12, true), "", new{data_description="birthDate"})@Html.LabelFor(m => m.StudentBirthMonth)</div>
    <div>@Html.DropDownListFor(m => m.Day, ApplicationModel.GetSelectListForDateRange(1, 31), "", new{data_description="birthDate"})@Html.LabelFor(m => m.StudentBirthDay)</div>
</div>
<div class="error-container">@Html.ValidationMessageFor(m => m.Year)</div>
<div class="error-container">@Html.ValidationMessageFor(m => m.Month)</div>
<div class="error-container">@Html.ValidationMessageFor(m => m.Day)</div>
4b9b3361

Ответ 1

Я немного опаздываю на вечеринку (всего пару лет)...

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

Пользовательский атрибут

public class GroupRequiredAttribute : ValidationAttribute, IClientValidatable
{
    private readonly string[] _serverSideProperties;

    public GroupRequiredAttribute(params string[] serverSideProperties)
    {
        _serverSideProperties = serverSideProperties;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (_serverSideProperties == null || _serverSideProperties.Length < 1)
        {
            return null;
        }

        foreach (var input in _serverSideProperties)
        {
            var propertyInfo = validationContext.ObjectType.GetProperty(input);
            if (propertyInfo == null)
            {
                return new ValidationResult(string.Format("unknown property {0}", input));
            }

            var propertyValue = propertyInfo.GetValue(validationContext.ObjectInstance, null);
            if (propertyValue is string && !string.IsNullOrEmpty(propertyValue as string))
            {
                return null;
            }

            if (propertyValue != null)
            {
                return null;
            }
        }

        return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var rule = new ModelClientValidationRule
        {
            ErrorMessage = ErrorMessage,
            ValidationType = "grouprequired"
        };

        rule.ValidationParameters["grouprequiredinputs"] = string.Join(",", this._serverSideProperties);

        yield return rule;
    }
}

ViewModel: Украсьте только одно поле в вашем представленииModel, как показано ниже:

    [GroupRequired("Year", "Month", "Day", ErrorMessage = "Please enter your date of birth")]
    public int? Year { get; set; }

    public int? Month { get; set; }

    public int? Day { get; set; }

JQuery: Вам нужно будет добавить адаптеры в моем случае jquery.validate.unobtrusive.customadapters.js или везде, где вы зарегистрируете свои адаптеры (вы можете поместить это на страницу, просто сделайте это после ненавязчивой проверки).

(function ($) {
    jQuery.validator.unobtrusive.adapters.add('grouprequired', ['grouprequiredinputs'], function (options) {
        options.rules['grouprequired'] = options.params;
        options.messages['grouprequired'] = options.message;
    });
}(jQuery));

jQuery.validator.addMethod('grouprequired', function (value, element, params) {
    var inputs = params.grouprequiredinputs.split(',');
    var values = $.map(inputs, function (input, index) {
        var val = $('#' + input).val();
        return val != '' ? input : null;
    });
    return values.length == inputs.length;
});

и это должно быть сделано.

Для тех, кому это интересно: на С# land он захватывает идентификаторы полей, склеивает их с помощью , и помещает в пользовательский атрибут в поле Year.

HTML должен выглядеть примерно так (если он не отлаживает атрибут С#):

<input class="tooltip form-control input dob--input-long" data-val="true" data-val-grouprequired="Please enter your date of birth" data-val-grouprequired-grouprequiredinputs="Year,Month,Day" name="Year" placeholder="YYYY" tabindex="" type="text" value="">

Затем проверка Jquery делит их обратно на id и проверяет, не все ли они пустые и что в значительной степени это.

Вы хотите как-то пометить поля как некорректные (теперь это будет только отметить, что атрибут поля сидит) наиболее подходящее решение IMHO состоит в том, чтобы обернуть все поля в контейнере с классом field-error-wrapper, а затем добавить следующее на вашу страницу после Jquery проверка достоверности:

$.validator.setDefaults({
    highlight: function (element) {
        $(element).closest(".field-error-wrapper").addClass("input-validation-error");
    },
    unhighlight: function (element) {
        $(element).closest(".field-error-wrapper").removeClass("input-validation-error");
    }
});

вместо поля маркировки будет помечен контейнер, а затем вы можете написать свой css таким образом, чтобы, если контейнер помечен .input-validation-error, тогда все поля внутри становятся красными. Я думаю, что моя работа здесь выполнена.

РЕДАКТИРОВАТЬ: Хорошо, поэтому появляется еще одна проблема, когда поля становятся немаркированными, потому что валидатор считает, что день и месяц действительны и ему нужно удалить недопустимый класс из родителя, валидатор сначала маркирует недопустимые поля, затем отменяет действие, которое заставляет валидацию не выделяться, поэтому я изменил последовательность, в которой происходит валидация, я бы не рекомендовал переопределять это глобально (потому что я не уверен в том, какая катастрофическая сторона может повлиять на нее) просто вставьте его на страницу, где у вас есть поля рождения.

$(function () {
    $.data($('form')[0], 'validator').settings.showErrors = function () {
        if (this.settings.unhighlight) {
            for (var i = 0, elements = this.validElements() ; elements[i]; i++) {
                this.settings.unhighlight.call(this, elements[i], this.settings.errorClass, this.settings.validClass);
            }
        }
        this.hideErrors();
        for (var i = 0; this.errorList[i]; i++) {
            var error = this.errorList[i];
            this.settings.highlight && this.settings.highlight.call(this, error.element, this.settings.errorClass, this.settings.validClass);
            this.showLabel(error.element, error.message);
        }
        if (this.errorList.length) {
            this.toShow = this.toShow.add(this.containers);
        }
        if (this.settings.success) {
            for (var i = 0; this.successList[i]; i++) {
                this.showLabel(this.successList[i]);
            }
        }
        this.toHide = this.toHide.not(this.toShow);

        this.addWrapper(this.toShow).show();
    };
});

Надеюсь, это сэкономит вам некоторое время.

Ответ 2

Вы должны реализовать IValidatableObject и взять Require. Затем проверка на стороне сервера будет выполнять задание, например:

public class MyModel : IValidatableObject
{
  public int Year { get; set; }
  public int Month { get; set; }
  public int Day { get; set; }

  public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
  {
      if (/*Validate properties here*/) yield return new ValidationResult("Invalid Date!", new[] { "valideDate" });
  }
}

Для проверки на стороне клиента вам необходимо реализовать свою собственную функцию и как-то запросить ошибку пользователю.

EDIT: учитывая, что вам все еще нужна проверка на стороне клиента, вы должны сделать что-то вроде этого:

$("form").validate({
    rules: {
        Day: { required: true },
        Month : { required: true },
        Year : { required: true }
    },
    groups: {
        Date: "Day Month Year"
    },
   errorPlacement: function(error, element) {
       if (element.attr("id") == "Day" || element.attr("id") == "Month" || element.attr("id") == "Year") 
        error.insertAfter("#Day");
       else 
        error.insertAfter(element);
   }
});

Ответ 3

Вы можете сделать это, просто используя CustomAttribute.

Просто поместите этот атрибут в свою модель

[CustomValidation(typeof(MyModel), "ValidateRelatedObject")]

а затем просто определите правила для проверки значений в следующем методе:

public static ValidationResult ValidateRelatedObject(object value, ValidationContext context)
{
    var context = new ValidationContext(value, validationContext.ServiceContainer, validationContext.Items);
    var results = new List<ValidationResult>();
    Validator.TryValidateObject(value, context, results);

    // TODO: Wrap or parse multiple ValidationResult into one ValidationResult

    return result; 
}

Для получения дополнительной информации вы можете посетить эту ссылку.

Ответ 4

Привет, надеюсь, это выполнит ваше требование

//--------------------------HTML Code-----------------------------
    <form id="myform">  
        <select name="day">
            <option value="">select</option>
            <option value="1">1</option>
        <select>
             <select name="mnth">
            <option value="">select</option>
            <option value="Jan">Jan</option>
        <select>
             <select name="yr">
            <option value="">select</option>
            <option value="2015">2015</option>
        <select>
            <br/>
        <input  type="submit"  />
                    <br/>
            <div id="msg"></div>
    </form>



    //-------------------------------JS Code to validate-------------



      $(document).ready(function() {


    $('#myform').validate({
        rules: {
            day: {
                required: true
            },
            mnth: {
                required: true
            },
             yr: {
                required: true
            }
        },
          errorPlacement: function (error, element) {
            var name = $(element).attr("name");
            error.appendTo($("#msg"));
        },
        messages: {
            day: {
                required: "Date of birth is required"
            },
            mnth: {
                required: "Date of birth is required"
            },
             yr: {
                required: "Date of birth is required"
            }
        },
        groups: {
            p: "day mnth yr"
        },
        submitHandler: function(form) { // for demo
            alert('valid form');
            return false;
        }
    });

});

Вот пример работы