我刚开始使用ASP.NET MVC 3,我试图在创建/编辑视图上为ViewModel上的字符串属性渲染以下HTML。
<input id="PatientID" name="PatientID" placeholder="Patient ID" type="text" value="" maxlength="30" />
每个值都与ViewModel上的属性绑定,id&amp; name是属性名称,占位符是Display属性,value是属性的值,maxlength是StringLength属性。
我没有输入上面的HTML,而是为每个字符串属性输入正确的值,我想我会尝试使用SingleLineTextBox的名称创建一个EditorTemplate,并在我的字符串属性上使用UIHint或者在传递视图的名称时我叫EditFor。到目前为止一切顺利,除了我无法弄清楚如何从StringLength属性中获取maxlength值。
这是我到目前为止的代码:
<input id="@ViewData.ModelMetadata.PropertyName" name="@ViewData.ModelMetadata.PropertyName" placeholder="@ViewData.ModelMetadata.DisplayName" type="text" value="@ViewData.Model" maxlength="??" />
如您所见,不确定如何设置maxlength值。谁知道怎么做?
另外,我是最好的方式吗?正如我之前所说,我可以自己为页面上的每个属性写出纯HTML。我已经看过使用TextBox因为它没有设置maxlength并且因为我不想要的StringLength属性而在HTML输出中添加了一堆验证标记。我看到的另一个选项是HTML类的扩展/帮助。
答案 0 :(得分:7)
tvanfosson答案的完整代码示例:
型号:
public class Product
{
public int Id { get; set; }
[MaxLength(200)]
public string Name { get; set; }
EditorTemplates \ String.cshtml
@model System.String
@{
var metadata = ViewData.ModelMetadata;
var prop = metadata.ContainerType.GetProperty(metadata.PropertyName);
var attrs = prop.GetCustomAttributes(false);
var maxLength = attrs.OfType<System.ComponentModel.DataAnnotations.MaxLengthAttribute>().FirstOrDefault();
}
<input id=@Html.IdForModel()@(metadata.IsRequired ? " required" : "")@(maxLength == null ? "" : " maxlength=" + maxLength.Length) />
HTML输出:
<input id=Name maxlength=200 />
丑陋但它有效。现在让我们抽象它并清理一下。助手班:
public static class EditorTemplateHelper
{
public static PropertyInfo GetPropertyInfo(ViewDataDictionary viewData)
{
var metadata = viewData.ModelMetadata;
var prop = metadata.ContainerType.GetProperty(metadata.PropertyName);
return prop;
}
public static object[] GetAttributes(ViewDataDictionary viewData)
{
var prop = GetPropertyInfo(viewData);
var attrs = prop.GetCustomAttributes(false);
return attrs;
}
public static string GenerateAttributeHtml(ViewDataDictionary viewData, IEnumerable<Delegate> attributeTemplates)
{
var attributeMap = attributeTemplates.ToDictionary(t => t.Method.GetParameters()[0].ParameterType, t => t);
var attrs = GetAttributes(viewData);
var htmlAttrs = attrs.Where(a => attributeMap.ContainsKey(a.GetType()))
.Select(a => attributeMap[a.GetType()].DynamicInvoke(a));
string s = String.Join(" ", htmlAttrs);
return s;
}
}
编辑模板:
@model System.String
@using System.ComponentModel.DataAnnotations;
@using Brass9.Web.Mvc.EditorTemplateHelpers;
@{
var metadata = ViewData.ModelMetadata;
var attrs = EditorTemplateHelper.GenerateAttributes(ViewData, new Delegate[] {
new Func<StringLengthAttribute, string>(len => "maxlength=" + len.MaximumLength),
new Func<MaxLengthAttribute, string>(max => "maxlength=" + max.Length)
});
if (metadata.IsRequired)
{
attrs.Add("required");
}
string attrsHtml = String.Join(" ", attrs);
}
<input type=text id=@Html.IdForModel() @attrsHtml />
所以你传入一个Delegates数组,并为每个条目使用Func<AttributeTypeGoesHere, string>
,然后为每个属性返回你想要的任何HTML字符串。
这实际上很好地解耦 - 您只能映射您关注的属性,您可以为同一HTML的不同部分映射不同的集合,最终用法(如@attrsHtml
)不会损害可读性模板。
答案 1 :(得分:5)
您可以使用StringLength
属性代替AdditionalMetadata
属性(因为它是验证程序属性而非元数据提供程序)。样品用法:
public class ViewModel
{
[AdditionalMetadata("maxLength", 30)]
public string Property { get; set; }
}
基本上它将值30置于ViewData.ModelMetadata.AdditionalValues
字典中的密钥maxLength下。所以你可以使用你的EditorTemplate:
<input maxlength="@ViewData.ModelMetadata.AdditionalValues["maxLength"]" id="@ViewData.ModelMetadata.PropertyName" name="@ViewData.ModelMetadata.PropertyName" placeholder="@ViewData.ModelMetadata.DisplayName" type="text" value="@ViewData.Model" />
答案 2 :(得分:2)
要执行此操作,您需要创建自己的HtmlHelper扩展并使用反射来获取模型属性上的属性。查看http://codeplex.com/aspnet处的源代码,了解现有的...For()
HtmlHelper扩展。您需要使用作为参数传入的表达式来获取model属性的PropertyInfo对象。他们有几个辅助类,应该作为模板。完成后,使用PropertyInfo上的GetCustomAttributes方法查找StringLength属性并提取它的值。由于您将使用TagBuilder创建输入,因此请通过TagBuilder将长度添加为属性。
...
var attribute = propInfo.GetCustomAttributes(typeof(StringLengthAttribute),false)
.OfType<StringLengthAttribute>()
.FirstOrDefault();
var length = attribute != null ? attribute.MaximumLength : 20; //provide a default
builder.Attributes.Add("maxlength",length);
...
return new MvcHtmlString( builder.ToString( TagRenderMode.SelfClosing ) );
}
请参阅我的评论,为什么我认为这是一个坏主意。
答案 3 :(得分:0)
更简单的解决方案是实现这样的自定义DataAnnotationsModelMetadataProvider
:
internal class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
ModelMetadata modelMetadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
var maxLengthAttribute = attributes.OfType<MaxLengthAttribute>().SingleOrDefault();
if (maxLengthAttribute != null)
{
modelMetadata.AdditionalValues.Add("maxLength", maxLengthAttribute.Length);
}
return modelMetadata;
}
}
在模板中,您只需使用:
object maxLength;
ViewData.ModelMetadata.AdditionalValues.TryGetValue("maxLength", out maxLength);
答案 4 :(得分:0)
您可以从编辑器模板中获取 StringLength 验证程序,以下是一些示例:
我在上面的文章中使用和测试的结果可以在下面的答案中看到(使用MVC 5测试,EF 6):
ASP.NET MVC 3 - Data Annoation and Max Length/Size for Textbox Rendering
没有具体说明,我个人尝试实施其他一些方法时遇到了一些混合的结果,而且我没有发现任何声称的方法特别长;然而,我确实认为其他一些接近看起来有点“漂亮”。
答案 5 :(得分:0)
@using System.ComponentModel.DataAnnotations
@model string
@{
var htmlAttributes = ViewData["htmlAttributes"] ?? new { @class = "checkbox-inline" };
var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
if (!attributes.ContainsKey("maxlength"))
{
var metadata = ViewData.ModelMetadata;
var prop = metadata.ContainerType.GetProperty(metadata.PropertyName);
var attrs = prop.GetCustomAttributes(false);
var maxLength = attrs.OfType<MaxLengthAttribute>().FirstOrDefault();
if (maxLength != null)
{
attributes.Add("maxlength", maxLength.Length.ToString());
}
else
{
var stringLength = attrs.OfType<StringLengthAttribute>().FirstOrDefault();
if (stringLength != null)
{
attributes.Add("maxlength", stringLength.MaximumLength.ToString());
}
}
}
}
@Html.TextBoxFor(m => m, attributes)