我是否可以在ASP.NET MVC5.2.2中使用接触点来扩展为视图模型的范围约束整数属性生成的HTML?
我在视图模型类中有一个整数属性,其范围约束在1到999之间。我想使用HTML5 min
和max
属性,以便浏览器提供的编辑器不允许用户输入超出该范围的值。
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" />
是否有任何现有方法可以将min
和max
属性添加到生成的输出中,该输出会获取属性“范围”属性中声明的值?我知道我可以手动添加属性作为additionalViewData
的{{1}}参数的一部分,但这是重复的,并提供了在cshtml和viewmodel代码之间漂移的能力。
我不必从头开始编写EditorFor模板.....
答案 0 :(得分:1)
没有开箱即用的功能,但您可以创建自己的扩展方法,也可以使用javascript / jQuery添加0.1
和min
属性。
扩展方法可能看起来像
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-range
和data-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"
}
})