带有.NET MVC 3 Razor Editor的HTML5占位符


92

有没有一种方法可以使用@ Html.EditorFor 编写Html5占位符,还是应该只使用TextBoxFor扩展名,即

@Html.TextBoxFor(model => model.Title, new { @placeholder = "Enter title here"})

还是编写自己的自定义扩展名可能有意义,该扩展名可以通过DataAnnotations使用“描述”显示属性(类似于this)?

当然,同样的问题也适用于“自动对焦”。

Answers:


68

您可以查看以下有关编写自定义的文章DataAnnotationsModelMetadataProvider

这是涉及新引入的IMetadataAware接口的另一种ASP.NET MVC 3ish继续方法。

首先创建实现此接口的自定义属性:

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>

感谢您提供IMetadataAware接口的信息(也是一个很好的例子)!
seekay 2011年

4
对MVC3仍然有效吗?我注意到MVC3中有一个新的[Display(Prompt =“ type watermark here”)],但无法使其正常工作。任何想法?
smnbss 2011年

2
@smnbss你是正确的。请参阅我的答案以了解如何进行Prompt工作。
Daniel Liuzzi

6
要做占位符需要做很多工作吗?必须更简单一些:S
krilovich 2013年

有,下面是一些答案。Pax有一个很好的。
Termato 2015年

121

正如达林·迪米特洛夫(Darin Dimitrov)回答中的smnbss注释Prompt正是出于此目的而存在的,因此无需创建自定义属性。从文档中:

获取或设置一个值,该值将用于为UI中的提示设置水印。

要使用它,只需装饰视图模型的属性,如下所示:

[Display(Prompt = "numbers only")]
public int Age { get; set; }

然后将该文本方便地放入中ModelMetadata.Watermark。开箱即用,MVC 3中的默认模板会忽略该Watermark属性,但是使其工作起来确实非常简单。您需要做的就是调整默认的字符串模板,以告诉MVC如何呈现它。只需像Darin一样编辑String.cshtml,除了除了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一点。

如果您还想为多行文本框(文本区域)启用水印功能,请对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 })

6
@Brett确实存在。EditorFor()是MVC 2中引入的模板化帮助器。乍一看,它看起来和TextBox()一样,但是它为您提供了一个巨大的优势,即允许您精确控制生成HTML的方式。我的答案是基于此功能来“教” MVC如何处理Prompt属性。有关这些模板的更多信息,请参考Brad Wilson的精彩文章:bradwilson.typepad.com/blog/2009/10/…–
Daniel Liuzzi

2
@DotNetWise我不确定你为什么这么说;DisplayAttribute(包括Prompt)的所有字符串参数都是可本地化的。您只需要在注释中指定ResourceType即可[Display(ResourceType = typeof(PeopleResources), Prompt = "AgePrompt")]。就是这样。水印的文字现在来自关键AgeGroup资源PeopleResources
Daniel Liuzzi 2012年

1
如果您不使用.resx资源而是使用i18N .po本地化系统怎么办?
Adaptabi

3
@FrancisRodgers EditorTemplates文件夹默认情况下不存在;您只需在Views\Shared文件夹中创建(或者Views\{ControllerName}如果您希望它特定于某个控制器)。然后,将.cshtml模板放置在此文件夹中,您应该会很好。
Daniel Liuzzi

2
@RobertIvanc我已经编辑了答案,并恢复了Raleigh Buckner所做的编辑,这些编辑引起您和Ted报告的问题。谢谢。
Daniel Liuzzi 2015年

22

实际上,我大部分时间更喜欢使用占位符文本的显示名称。这是使用DisplayName的示例:

  @Html.TextBoxFor(x => x.FirstName, true, null, new { @class = "form-control", placeholder = Html.DisplayNameFor(x => x.FirstName) })

1
有一个特殊的数据注释,提示有水印。DisplayName用于字段标签。混合它们是一个坏主意。将正确的事情用于正确的任务。看我的回答。
Mike Eshva

1
谢谢,这就是我一直在寻找的东西,简单到可以获取显示名称的地步,然后为什么要添加更多的类
saqibahmad15年

这将两次转义DisplayName提供的文本-对于带有法语等重音的示例语言来说,这不是一个很好的解决方案。
marapet

3

我写了一个简单的类:

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参数。


只需反编译DisplayNameFor方法并为水印做一个模拟。
Mike Eshva

嗨,如果未指定显示->提示值,可以将方法MvcHtmlString WatermarkFor()更改为使用DisplayName属性值吗?
2015年

您将WatermarkExtension类保存在哪里,以便可以按照您的描述使用它?Html.WatermarkFor(model => model.AddressSuffix)
Craig Gjerdingen 2015年

3

我对资源文件使用了这种方式(不再需要Prompt了!)

@Html.TextBoxFor(m => m.Name, new 
{
     @class = "form-control",
     placeholder = @Html.DisplayName(@Resource.PleaseTypeName),
     autofocus = "autofocus",
     required = "required"
})

1

这是我使用上述想法制作的解决方案,可用于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));
    }
}

0

我认为创建自定义EditorTemplate并不是一个好的解决方案,因为您需要关心许多不同情况下的提示:字符串,数字,组合框等。其他解决方案是自定义扩展到HtmlHelper。

模型:

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

HTML Helper扩展:

   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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.