问题


在MVC的view页面上,我们虽然可以通过给html attribute手写new { @placeholder = "something" }来增加placeholder属性,但这样做对于MVC3框架而言不是非常地道。一个比较好的办法是通过拓展MVC,来增加一个DataAnnotation属性,最终在Model中指定PlaceHolder。下面的方法是我在stackoverflow网站上撸到的,稍作修改并测试后发现非常牛逼,分享给大家。

解决方法


首先,我们要码一个PlaceHolderAttribute类,让它继承Attribute, IMetadataAware两个接口。注意类名是包含Attribute后缀的,但最终使用的时候是只取PlaceHolder这个名字的。(这个机制是.NET自己撸的,我们不用管)

public class PlaceHolderAttribute : Attribute, IMetadataAware
{
    private readonly string _placeholder;
    public PlaceHolderAttribute(string placeholder)
    {
        _placeholder = placeholder;
    }

    public void OnMetadataCreated(ModelMetadata metadata)
    {
        metadata.AdditionalValues["placeholder"] = _placeholder;
    }
}

这个类你可以丢在类库里或MVC网站项目文件夹里,最后要注意一下引用和命名空间。我的习惯是把这些Attribute类都放在网站下的Infrastructures文件夹中。

但现在还不够,我们必须自定义一个PlaceHolder的显示模板。建一个string类型的显示模板,放到“~/Views/Shared/EditorTemplates/string.cshtml”里:

@{
    var placeholder = string.Empty;
    if (ViewData.ModelMetadata.AdditionalValues.ContainsKey("placeholder"))
    {
        placeholder = ViewData.ModelMetadata.AdditionalValues["placeholder"] as string;
    }
}
@Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { placeholder = placeholder })

这个模板可以根据你的需求充分自定义。

接下来,我们就可以在Model中用这个placeholder特征了:

Model:

public class MyModel
{
    [PlaceHolder("Enter title here")]
    public string Title { get; set; }
}

Controller:

public ActionResult Index()
{
    var model = new MyModel();
    return View(model);
}

View:

@model MVCPlaceHolderDemo.Models.MyModel
<p>
    @Html.EditorFor(x => x.Title)
</p>

最后效果如下:

为了兼容不同浏览器,我们还需要先检查一下浏览器是否支持PlaceHolder属性,如果不行就用JQuery加上。配合MVC3默认模板里自带的Modernizr,可以在layout页面加上这段脚本:

$(document).ready(function () {
    if (!Modernizr.input.placeholder) {
        $("input").each(
        function () {
            if ($(this).val() == "" && $(this).attr("placeholder") != "") {
                $(this).val($(this).attr("placeholder"));
                $(this).focus(function () {
                    if ($(this).val() == $(this).attr("placeholder")) $(this).val("");
                });
                $(this).blur(function () {
                    if ($(this).val() == "") $(this).val($(this).attr("placeholder"));
                });
            }
        });
    }
});

这样就可以兼容了~