MVC使用[Range]属性中的HTML5 min max值扩展Html.EditorFor(int)

时间:2017-10-21 17:46:53

标签: c# asp.net-mvc html5

我是否可以在ASP.NET MVC5.2.2中使用接触点来扩展为视图模型的范围约束整数属性生成的HTML?

我在视图模型类中有一个整数属性,其范围约束在1到999之间。我想使用HTML5 minmax属性,以便浏览器提供的编辑器不允许用户输入超出该范围的值。

e.g。在Chrome中,<input type="number" min="1" max="999" />为我提供了一个旋转器的字段,其值限制为1到999.

目前,jquery不显眼的验证正在强制执行规则,突出显示超出范围的值,但如果微调器首先没有创建无效值,那就更好了。

Viewmodel类:

public class MyViewModel
{
  [Required]
  [Range(1, 999)]
  public int? ValueInRange
  {
      get;
      set;
  }
}

ValueInRange编辑器的cshtml:

@Html.LabelFor(m => m.ValueInRange)
@Html.EditorFor(m => m.ValueInRange)
@Html.ValidationMessageFor(m => m.ValueInRange)

生成的HTML:

<input type="number" value="0" id="ValueInRange" name="ValueInRange"
    class="form-control text-box single-line" 
    data-val="true" 
    data-val-number="The field ValueInRange must be a number." 
    data-val-range="Please select a value between 1 and 999" 
    data-val-range-max="999" 
    data-val-range-min="1" 
    data-val-required="ValueInRange is required" />

是否有任何现有方法可以将minmax属性添加到生成的输出中,该输出会获取属性“范围”属性中声明的值?我知道我可以手动添加属性作为additionalViewData的{​​{1}}参数的一部分,但这是重复的,并提供了在cshtml和viewmodel代码之间漂移的能力。

我不必从头开始编写EditorFor模板.....

4 个答案:

答案 0 :(得分:1)

没有开箱即用的功能,但您可以创建自己的扩展方法,也可以使用javascript / jQuery添加0.1min属性。

扩展方法可能看起来像

max

您可以通过检查属性类型是数字类型(例如these answers),为public static MvcHtmlString NumericRangeInputFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes = null) { // Add type="number" var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes); attributes.Add("type", "number"); // Check for a [Range] attribute ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, helper.ViewData); var property = metadata.ContainerType.GetProperty(metadata.PropertyName); RangeAttribute range = property.GetCustomAttributes(typeof(RangeAttribute), false).First() as RangeAttribute; if (range != null) { attributes.Add("min", range.Minimum); attributes.Add("max", range.Maximum); } return helper.TextBoxFor(expression, attributes); } 属性添加参数等来增强此功能。

使用jQuery,您可以检查step属性,并根据data-val-rangedata-val-range-min属性添加值

data-val-range-max

答案 1 :(得分:1)

我已经从.net corefx中提取了范围验证器并创建了一个自定义范围验证器,因为很难为Microsoft专家添加两行代码。只需将其保存在某处,相应地调整名称空间,并将其作为[Html5Range()]样式包含在模型中。不喜欢JS方式。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;

namespace Admin.Validators
{
    public class Html5RangeAttribute : ValidationAttribute, IClientModelValidator
    {
        private  string _max;
        private  string _min;

        private object Minimum { get;  set; }
        private object Maximum { get;  set; }

        private Type OperandType { get; }
        private bool ParseLimitsInInvariantCulture { get; set; }
        private bool ConvertValueInInvariantCulture { get; set; }

        public Html5RangeAttribute(int minimum, int maximum)
        {
            Minimum = minimum;
            Maximum = maximum;
            OperandType = typeof(int);
        }

        public Html5RangeAttribute(double minimum, double maximum)
        {
            Minimum = minimum;
            Maximum = maximum;
            OperandType = typeof(double);
        }

        public Html5RangeAttribute(Type type, string minimum, string maximum)
        {
            OperandType = type;
            Minimum = minimum;
            Maximum = maximum;
        }       

        private Func<object, object> Conversion { get; set; }

        private void Initialize(IComparable minimum, IComparable maximum, Func<object, object> conversion)
        {
            if (minimum.CompareTo(maximum) > 0)
            {
                throw new InvalidOperationException(string.Format("The maximum value '{0}' must be greater than or equal to the minimum value '{1}'.", maximum, minimum));
            }

            Minimum = minimum;
            Maximum = maximum;
            Conversion = conversion;
        }

        public override bool IsValid(object value)
        {            
            SetupConversion();

            if (value == null || (value as string)?.Length == 0)
            {
                return true;
            }

            object convertedValue;

            try
            {
                convertedValue = Conversion(value);
            }
            catch (FormatException)
            {
                return false;
            }
            catch (InvalidCastException)
            {
                return false;
            }
            catch (NotSupportedException)
            {
                return false;
            }

            var min = (IComparable)Minimum;
            var max = (IComparable)Maximum;
            return min.CompareTo(convertedValue) <= 0 && max.CompareTo(convertedValue) >= 0;
        }

        public override string FormatErrorMessage(string name)
        {
            SetupConversion();

            return string.Format(CultureInfo.CurrentCulture, ErrorMessageString, name, Minimum, Maximum);
        }

        private void SetupConversion()
        {
            if (Conversion == null)
            {
                object minimum = Minimum;
                object maximum = Maximum;

                if (minimum == null || maximum == null)
                {
                    throw new InvalidOperationException("The minimum and maximum values must be set.");
                }

                // Careful here -- OperandType could be int or double if they used the long form of the ctor.
                // But the min and max would still be strings.  Do use the type of the min/max operands to condition
                // the following code.
                Type operandType = minimum.GetType();

                if (operandType == typeof(int))
                {
                    Initialize((int) minimum, (int) maximum, v => Convert.ToInt32(v, CultureInfo.InvariantCulture));
                }
                else if (operandType == typeof(double))
                {
                    Initialize((double) minimum, (double) maximum,
                        v => Convert.ToDouble(v, CultureInfo.InvariantCulture));
                }
                else
                {
                    Type type = OperandType;
                    if (type == null)
                    {
                        throw new InvalidOperationException("The OperandType must be set when strings are used for minimum and maximum values.");
                    }

                    Type comparableType = typeof(IComparable);
                    if (!comparableType.IsAssignableFrom(type))
                    {
                        throw new InvalidOperationException(string.Format("The type {0} must implement {1}.",
                            type.FullName,
                            comparableType.FullName));
                    }

                    TypeConverter converter = TypeDescriptor.GetConverter(type);
                    IComparable min = (IComparable) (ParseLimitsInInvariantCulture
                        ? converter.ConvertFromInvariantString((string) minimum)
                        : converter.ConvertFromString((string) minimum));
                    IComparable max = (IComparable) (ParseLimitsInInvariantCulture
                        ? converter.ConvertFromInvariantString((string) maximum)
                        : converter.ConvertFromString((string) maximum));

                    Func<object, object> conversion;
                    if (ConvertValueInInvariantCulture)
                    {
                        conversion = value => value.GetType() == type
                            ? value
                            : converter.ConvertFrom(null, CultureInfo.InvariantCulture, value);
                    }
                    else
                    {
                        conversion = value => value.GetType() == type ? value : converter.ConvertFrom(value);
                    }

                    Initialize(min, max, conversion);
                }
            }
        }

        public  void AddValidation(ClientModelValidationContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            _max = Convert.ToString(Maximum, CultureInfo.InvariantCulture);
            _min = Convert.ToString(Minimum, CultureInfo.InvariantCulture);

            MergeAttribute(context.Attributes, "data-val", "false");
            MergeAttribute(context.Attributes, "data-val-range", GetErrorMessage(context));
            MergeAttribute(context.Attributes, "data-val-range-max", _max);
            MergeAttribute(context.Attributes, "data-val-range-min", _min);
            MergeAttribute(context.Attributes, "min", _min);
            MergeAttribute(context.Attributes, "max", _max);
        }

        private static  bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
        {
            if (attributes.ContainsKey(key))
            {
                return false;
            }

            attributes.Add(key, value);
            return true;
        }

        private string GetErrorMessage(ModelValidationContextBase validationContext)
        {
            if (validationContext == null)
            {
                throw new ArgumentNullException(nameof(validationContext));
            }

            return string.Format("The field {0} must be between {1} and {2}.", validationContext.ModelMetadata.GetDisplayName(), Minimum, Maximum );
        }
    }
    }

答案 2 :(得分:0)

ES5 vanilla js版本:

sleep()

})();

答案 3 :(得分:0)

我认为这种方法是有效的。

  @Html.EditorFor(model => model.ValueInRange, new {  
    htmlAttributes = new { 
       @class = "form-control", min="1" 
        } 
    })