有没有办法使用@Html.EditorFor 编写Html5 占位符,或者我应该只使用 TextBoxFor 扩展,即
@Html.TextBoxFor(model => model.Title, new { @placeholder = "Enter title here"})
或者编写我们自己的自定义扩展是否有意义,它可以通过 DataAnnotations 使用“描述”显示属性(类似于这个)?
当然,同样的问题也适用于“自动对焦”。
正如 Darin Dimitrov 的回答中的 smnbss 评论,Prompt
正是为了这个目的而存在的,所以不需要创建自定义属性。从文档中:
获取或设置一个值,该值将用于为 UI 中的提示设置水印。
要使用它,只需像这样装饰视图模型的属性:
[Display(Prompt = "numbers only")]
public int Age { get; set; }
该文本然后方便地放置在ModelMetadata.Watermark
. 开箱即用,MVC 3 中的默认模板忽略该Watermark
属性,但使其工作非常简单。你需要做的就是调整默认的字符串模板,告诉 MVC 如何渲染它。只需编辑 String.cshtml,就像 Darin 所做的那样,除了不是从 获取水印ModelMetadata.AdditionalValues
,而是直接从 获取它ModelMetadata.Watermark
:
~/Views/Shared/EditorTemplates/String.cshtml:
@Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { @class = "text-box single-line", placeholder = ViewData.ModelMetadata.Watermark })
就是这样。
如您所见,使一切正常运行的关键是placeholder = ViewData.ModelMetadata.Watermark
位。
如果您还想为多行文本框 (textareas) 启用水印,请对 MultilineText.cshtml 执行相同操作:
~/Views/Shared/EditorTemplates/MultilineText.cshtml:
@Html.TextArea("", ViewData.TemplateInfo.FormattedModelValue.ToString(), 0, 0, new { @class = "text-box multi-line", placeholder = ViewData.ModelMetadata.Watermark })
您可以查看以下文章来编写自定义DataAnnotationsModelMetadataProvider
.
这是另一种更多的 ASP.NET MVC 3ish 方式,涉及新引入的IMetadataAware接口。
首先创建一个实现此接口的自定义属性:
public class PlaceHolderAttribute : Attribute, IMetadataAware
{
private readonly string _placeholder;
public PlaceHolderAttribute(string placeholder)
{
_placeholder = placeholder;
}
public void OnMetadataCreated(ModelMetadata metadata)
{
metadata.AdditionalValues["placeholder"] = _placeholder;
}
}
然后用它装饰你的模型:
public class MyViewModel
{
[PlaceHolder("Enter title here")]
public string Title { get; set; }
}
接下来定义一个控制器:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel());
}
}
对应的视图:
@model MyViewModel
@using (Html.BeginForm())
{
@Html.EditorFor(x => x.Title)
<input type="submit" value="OK" />
}
最后是编辑器模板 ( ~/Views/Shared/EditorTemplates/string.cshtml
):
@{
var placeholder = string.Empty;
if (ViewData.ModelMetadata.AdditionalValues.ContainsKey("placeholder"))
{
placeholder = ViewData.ModelMetadata.AdditionalValues["placeholder"] as string;
}
}
<span>
@Html.Label(ViewData.ModelMetadata.PropertyName)
@Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { placeholder = placeholder })
</span>
我实际上更喜欢在大多数情况下使用占位符文本的显示名称。以下是使用 DisplayName 的示例:
@Html.TextBoxFor(x => x.FirstName, true, null, new { @class = "form-control", placeholder = Html.DisplayNameFor(x => x.FirstName) })
我对资源文件使用这种方式(不再需要提示!)
@Html.TextBoxFor(m => m.Name, new
{
@class = "form-control",
placeholder = @Html.DisplayName(@Resource.PleaseTypeName),
autofocus = "autofocus",
required = "required"
})
我写了这样一个简单的类:
public static class WatermarkExtension
{
public static MvcHtmlString WatermarkFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
{
var watermark = ModelMetadata.FromLambdaExpression(expression, html.ViewData).Watermark;
var htmlEncoded = HttpUtility.HtmlEncode(watermark);
return new MvcHtmlString(htmlEncoded);
}
}
用法如下:
@Html.TextBoxFor(model => model.AddressSuffix, new {placeholder = Html.WatermarkFor(model => model.AddressSuffix)})
和视图模型中的属性:
[Display(ResourceType = typeof (Resources), Name = "AddressSuffixLabel", Prompt = "AddressSuffixPlaceholder")]
public string AddressSuffix
{
get { return _album.AddressSuffix; }
set { _album.AddressSuffix = value; }
}
注意提示参数。在这种情况下,我使用资源中的字符串进行本地化,但您可以只使用字符串,只需避免使用 ResourceType 参数。
这是我使用上述想法制作的解决方案,可用于 TextBoxFor 和 PasswordFor:
public static class HtmlHelperEx
{
public static MvcHtmlString TextBoxWithPlaceholderFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
return htmlHelper.TextBoxFor(expression, htmlAttributes.AddAttribute("placeholder", metadata.Watermark));
}
public static MvcHtmlString PasswordWithPlaceholderFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
return htmlHelper.PasswordFor(expression, htmlAttributes.AddAttribute("placeholder", metadata.Watermark));
}
}
public static class HtmlAttributesHelper
{
public static IDictionary<string, object> AddAttribute(this object htmlAttributes, string name, object value)
{
var dictionary = htmlAttributes == null ? new Dictionary<string, object>() : htmlAttributes.ToDictionary();
if (!String.IsNullOrWhiteSpace(name) && value != null && !String.IsNullOrWhiteSpace(value.ToString()))
dictionary.Add(name, value);
return dictionary;
}
public static IDictionary<string, object> ToDictionary(this object obj)
{
return TypeDescriptor.GetProperties(obj)
.Cast<PropertyDescriptor>()
.ToDictionary(property => property.Name, property => property.GetValue(obj));
}
}
我认为创建自定义 EditorTemplate 不是一个好的解决方案,因为您需要关心针对不同情况的许多可能的模板:字符串、numsers、组合框等。其他解决方案是自定义扩展 HtmlHelper。
模型:
public class MyViewModel
{
[PlaceHolder("Enter title here")]
public string Title { get; set; }
}
Html 辅助扩展:
public static MvcHtmlString BsEditorFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TValue>> expression, string htmlClass = "")
{
var modelMetadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
var metadata = modelMetadata;
var viewData = new
{
HtmlAttributes = new
{
@class = htmlClass,
placeholder = metadata.Watermark,
}
};
return htmlHelper.EditorFor(expression, viewData);
}
对应的视图:
@Html.BsEditorFor(x => x.Title)