HTML5 Заполнители с.NET MVC 3 Razor Editor для расширения?

Есть ли способ написать заполнитель Html5, используя @Html.EditorFor, или я должен просто использовать расширение TextBoxFor, т.е.

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

Или имеет смысл написать собственное расширение, которое может использовать атрибут отображения "Description" через DataAnnotations (аналогично этому)?

Конечно, то же самое относится и к "автофокусу".

8 ответов

Решение

Вы можете взглянуть на следующую статью для написания пользовательских DataAnnotationsModelMetadataProvider,

И вот еще один, более ASP.NET MVC 3-й способ продолжения работы с использованием недавно представленного интерфейса 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>

Как комментирует smnbss в ответе Дарина Димитрова, Prompt существует именно для этой цели, поэтому нет необходимости создавать пользовательский атрибут. Из документации:

Получает или задает значение, которое будет использоваться для установки водяного знака для приглашений в пользовательском интерфейсе.

Чтобы использовать его, просто украсьте свойство вашей модели вида следующим образом:

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

Этот текст затем удобно помещается в ModelMetadata.Watermark, Из коробки стандартный шаблон в MVC 3 игнорирует Watermark собственности, но заставить это работать действительно просто. Все, что вам нужно сделать, это настроить шаблон строки по умолчанию, чтобы сообщить MVC, как его визуализировать. Просто отредактируйте 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 })

Я на самом деле предпочитаю использовать отображаемое имя для текста заполнителя в большинстве случаев. Вот пример использования 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)})

И недвижимость в viewmodel:

[Display(ResourceType = typeof (Resources), Name = "AddressSuffixLabel", Prompt = "AddressSuffixPlaceholder")]
public string AddressSuffix
{
    get { return _album.AddressSuffix; }
    set { _album.AddressSuffix = value; }
}

Обратите внимание на параметр Prompt. В этом случае я использую строки из ресурсов для локализации, но вы можете использовать только строки, просто избегайте параметра 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 не является хорошим решением, потому что вам нужно заботиться о многих возможных шаблонах для разных случаев: строк, чисел, выпадающих списков и так далее. Другое решение - пользовательское расширение для 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)
@Html.TextBoxFor(model => model.Message,  new { @class = "form-control"  , @placeholder = "Message.." })
Другие вопросы по тегам