Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
378 views
in Technique[技术] by (71.8m points)

asp.net mvc - MVC unobtrusive range validation of dynamic values

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
{
    int Value { get; set; }

    int MinValue { get; set; }

    int MaxValue { get; set; }
}

Of course, I can't pass these Min/MaxValues into my DataAnnotations attributes, as they have to be constant values.

I'm sure I need to build my own validation attribute, but I haven't done this much and can't wrap my mind around how it should work.

I've searched for about an hour, and have seen all sorts of solutions for building custom validation, but can't find anything to solve this particular problem using MVC3 unobtrusive validation.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...