MVC unobtrusive range validation of dynamic values

后端 未结 3 1159
无人及你
无人及你 2020-12-05 03:09

I have a value on my model, that must fall within the range of two other values on my model.

For example:

public class RangeValidationSampleModel
{
          


        
相关标签:
3条回答
  • 2020-12-05 03:36

    If you need client side validation this will have to be custom. I saw a nice post on here recently (Darin Dmitrov? Cant seem to find it) Anyways - this will allow client validation to occur: http://blogs.msdn.com/b/simonince/archive/2011/02/04/conditional-validation-in-asp-net-mvc-3.aspx

    Server side can be handled via IValidateableObject or Dynamic Range Validation in ASP.NET MVC 2

    etc etc on the server side but I feel you want client side here to be the key.

    0 讨论(0)
  • 2020-12-05 03:39

    You could write a custom validation attribute for this purpose:

    public class DynamicRangeValidator : ValidationAttribute, IClientValidatable
    {
        private readonly string _minPropertyName;
        private readonly string _maxPropertyName;
        public DynamicRangeValidator(string minPropertyName, string maxPropertyName)
        {
            _minPropertyName = minPropertyName;
            _maxPropertyName = maxPropertyName;
        }
    
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            var minProperty = validationContext.ObjectType.GetProperty(_minPropertyName);
            var maxProperty = validationContext.ObjectType.GetProperty(_maxPropertyName);
            if (minProperty == null)
            {
                return new ValidationResult(string.Format("Unknown property {0}", _minPropertyName));
            }
            if (maxProperty == null)
            {
                return new ValidationResult(string.Format("Unknown property {0}", _maxPropertyName));
            }
    
            int minValue = (int)minProperty.GetValue(validationContext.ObjectInstance, null);
            int maxValue = (int)maxProperty.GetValue(validationContext.ObjectInstance, null);
            int currentValue = (int)value;
            if (currentValue <= minValue || currentValue >= maxValue)
            {
                return new ValidationResult(
                    string.Format(
                        ErrorMessage, 
                        minValue,
                        maxValue
                    )
                );
            }
    
            return null;
        }
    
        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            var rule = new ModelClientValidationRule
            {
                ValidationType = "dynamicrange",
                ErrorMessage = this.ErrorMessage,
            };
            rule.ValidationParameters["minvalueproperty"] = _minPropertyName;
            rule.ValidationParameters["maxvalueproperty"] = _maxPropertyName;
            yield return rule;
        }
    }
    

    and then decorate your view model with it:

    public class RangeValidationSampleModel
    {
        [DynamicRangeValidator("MinValue", "MaxValue", ErrorMessage = "Value must be between {0} and {1}")]
        public int Value { get; set; }
        public int MinValue { get; set; }
        public int MaxValue { get; set; }
    }
    

    then you could have a controller serving a view:

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View(new RangeValidationSampleModel
            {
                Value = 5,
                MinValue = 6,
                MaxValue = 8
            });
        }
    
        [HttpPost]
        public ActionResult Index(RangeValidationSampleModel model)
        {
            return View(model);
        }
    }
    

    and a view of course:

    @model RangeValidationSampleModel
    
    <script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
    <script type="text/javascript">
        $.validator.unobtrusive.adapters.add('dynamicrange', ['minvalueproperty', 'maxvalueproperty'],
            function (options) {
                options.rules['dynamicrange'] = options.params;
                if (options.message != null) {
                    $.validator.messages.dynamicrange = options.message;
                }
            }
        );
    
        $.validator.addMethod('dynamicrange', function (value, element, params) {
            var minValue = parseInt($('input[name="' + params.minvalueproperty + '"]').val(), 10);
            var maxValue = parseInt($('input[name="' + params.maxvalueproperty + '"]').val(), 10);
            var currentValue = parseInt(value, 10);
            if (isNaN(minValue) || isNaN(maxValue) || isNaN(currentValue) || minValue >= currentValue || currentValue >= maxValue) {
                var message = $(element).attr('data-val-dynamicrange');
                $.validator.messages.dynamicrange = $.validator.format(message, minValue, maxValue);
                return false;
            }
            return true;
        }, '');
    </script>
    
    @using (Html.BeginForm())
    {
        <div>
            @Html.LabelFor(x => x.Value)
            @Html.EditorFor(x => x.Value)
            @Html.ValidationMessageFor(x => x.Value)
        </div>
        <div>
            @Html.LabelFor(x => x.MinValue)
            @Html.EditorFor(x => x.MinValue)
        </div>
        <div>
            @Html.LabelFor(x => x.MaxValue)
            @Html.EditorFor(x => x.MaxValue)
        </div>
        <button type="submit">OK</button>
    }
    

    Obviously the custom adapter registration should be performed in an external javascript file to avoid polluting the view but for the purpose and conciseness of this post I have put it inside the view.

    0 讨论(0)
  • 2020-12-05 03:41

    custom validation attributes are indeed a good thought. something like (digging up some snippet o'mine found who knows where a while ago):

    public sealed class MustBeGreaterThan : ValidationAttribute
    {
        private const string _defaultErrorMessage = "'{0}' must be greater than '{1}'";
        private string _basePropertyName;
    
        public MustBeGreaterThan(string basePropertyName)
            : base(_defaultErrorMessage)
        {
            _basePropertyName = basePropertyName;
        }
    
        //Override default FormatErrorMessage Method
        public override string FormatErrorMessage(string name)
        {
            return string.Format(_defaultErrorMessage, name, _basePropertyName);
        }
    
        //Override IsValid
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            var basePropertyInfo = validationContext.ObjectType.GetProperty(_basePropertyName);
            var lowerBound = (int)basePropertyInfo.GetValue(validationContext.ObjectInstance, null);
            var thisValue = (int)value;
    
            if (thisValue < lowerBound)
            {
                var message = FormatErrorMessage(validationContext.DisplayName);
                return new ValidationResult(message);
            }
    
            //value validated
            return null;
        }
    }
    
    public sealed class MustBeLowerThan : ValidationAttribute
    {
        private const string _defaultErrorMessage = "'{0}' must be lower than '{1}'";
        private string _basePropertyName;
    
        public MustBeLowerThan(string basePropertyName)
            : base(_defaultErrorMessage)
        {
            _basePropertyName = basePropertyName;
        }
    
        //Override default FormatErrorMessage Method
        public override string FormatErrorMessage(string name)
        {
            return string.Format(_defaultErrorMessage, name, _basePropertyName);
        }
    
        //Override IsValid
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            var basePropertyInfo = validationContext.ObjectType.GetProperty(_basePropertyName);
            var upperBound = (int)basePropertyInfo.GetValue(validationContext.ObjectInstance, null);
            var thisValue = (int)value;
    
            if (thisValue > upperBound)
            {
                var message = FormatErrorMessage(validationContext.DisplayName);
                return new ValidationResult(message);
            }
    
            //value validated
            return null;
        }
    }
    

    then decorate your class

    public class RangeValidationSampleModel
    {
        [MustBeGreaterThan("MinValue")]
        [MustBeLowerThan("MaxValue")]
        int Value { get; set; }
    
        int MinValue { get; set; }
    
        int MaxValue { get; set; }
    }
    

    and you should be good to go

    0 讨论(0)
提交回复
热议问题