【问题标题】:Html5 Placeholders with .NET MVC 3 Razor EditorFor extension?带有 .NET MVC 3 Razor EditorFor 扩展的 Html5 占位符?
【发布时间】:2023-03-18 21:40:01
【问题描述】:

有没有办法使用@Html.EditorFor 编写Html5 placeholder,或者我应该只使用TextBoxFor 扩展,即

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

或者编写我们自己的自定义扩展,可以通过 DataAnnotations 使用“描述”显示属性(类似于this)是否有意义?

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

【问题讨论】:

    标签: asp.net-mvc html editorfor


    【解决方案1】:

    我认为创建自定义 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)
    

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        【解决方案3】:

        正如 Darin Dimitrov 的回答中的 smnbss cmets,Prompt 正是为此目的而存在,因此无需创建自定义属性。从文档中:

        获取或设置一个将被使用的值 为提示设置水印 用户界面。

        要使用它,只需像这样装饰视图模型的属性:

        [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 位。

        如果您还想为多行文本框 (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 })
        

        【讨论】:

        • @Brett 确实有。 EditorFor() 是 MVC 2 中引入的一个 模板化 帮助器。乍一看,它可能与 TextBox() 做同样的事情,但它为您提供了一个很大的优势,即允许您准确控制您的操作方式想要生成您的 HTML。我的回答是基于这个特性来“教”MVC 如何处理Prompt 属性。有关这些模板的更多信息,您可以参考 Brad Wilson 的这篇精彩文章:bradwilson.typepad.com/blog/2009/10/…
        • @DotNetWise 我不知道你为什么这么说; DisplayAttribute(包括 Prompt)的所有字符串参数都是可本地化的。您只需在注释中指定 ResourceType:[Display(ResourceType = typeof(PeopleResources), Prompt = "AgePrompt")]。就是这样。水印文本现在来自资源 PeopleResources 中的键 AgeGroup
        • 如果您使用的不是 .resx 资源而是 i18N .po 本地化系统怎么办?
        • @FrancisRodgers EditorTemplates 文件夹默认不存在;您只需在 Views\Shared 文件夹中创建(或 Views\{ControllerName},如果您希望它特定于某个控制器)。然后,您将 .cshtml 模板放置在此文件夹中,您应该一切顺利。
        • @RobertIvanc 我已经编辑了答案并恢复了 Raleigh Buckner 所做的导致您和 Ted 报告的问题的编辑。谢谢。
        【解决方案4】:

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

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

        【讨论】:

          【解决方案5】:

          我写了这么简单的类:

          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 方法并为水印做了一个模拟。
          • 嗨,如果没有指定 Display -> Prompt 值,您能否更改您的方法 MvcHtmlString WatermarkFor() 以使用 DisplayName 属性值?
          • 你在哪里保存你的 WatermarkExtension 类,以便它可以按照你的描述使用? Html.WatermarkFor(model => model.AddressSuffix)
          【解决方案6】:

          实际上,我大多数时候更喜欢使用占位符文本的显示名称。以下是使用 DisplayName 的示例:

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

          【讨论】:

          • 有一个特殊的数据注释提示水印。 DisplayName 用于字段标签。混合它们是个坏主意。为正确的任务使用正确的事物。看我的回答。
          • 谢谢,这就是我一直在寻找的,简单到我们可以得到显示名称的地步,那么为什么要添加更多类
          • 这将双重转义 DisplayName 提供的文本 - 对于带有法语等重音的语言来说,这不是一个好的解决方案。
          【解决方案7】:

          您可以查看following article 以编写自定义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>
          

          【讨论】:

          • 感谢 IMetadataAware 接口的信息(和一个很好的例子)!
          • 这对 MVC3 仍然有效吗?我注意到 MVC3 中有一个新的 [Display(Prompt = "type watermark here")] 但无法使其工作。有什么想法吗?
          • @smnbss 你是对的。请参阅我的答案以了解如何使 Prompt 工作。
          • 哇,做占位符的工作量这么大?必须更简单:S
          • 有,看看下面的一些答案。 Pax 有一个很好的。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-05-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-01-31
          相关资源
          最近更新 更多