HTML-заповнювачі з .NET MVC 3 Razor EditorДля розширення?


92

Чи є спосіб написати заповнювач Html5 за допомогою @ Html.EditorFor, чи я повинен просто використовувати розширення TextBoxFor, тобто

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

Або має сенс написати власне власне розширення, яке може, можливо, використовувати атрибут відображення "Опис" через DataAnnotations (подібне до цього )?

Звичайно, те саме питання стосується і „автофокусу”.

Відповіді:


68

Ви можете поглянути на наступну статтю, щоб написати звичай 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!
seekay

4
це все ще діє для MVC3? Я помітив новий [Відображення (Підказка = "введіть тут водяний знак")] у MVC3, але не зміг змусити його працювати. будь-яка ідея?
smnbss

2
@smnbss Ви маєте рацію. Дивіться мою відповідь, щоб дізнатись, як змусити Promptпрацювати.
Даніель Люцці

6
вау так багато роботи для виконання заповнювача? повинно бути щось простіше: S
krilovich

Є, подивіться на деякі відповіді нижче. У Pax є хороший.
Termato

121

Як 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біт.

Якщо ви також хочете увімкнути водяний знак для багаторядкових текстових полів (текстових областей), ви зробите те саме для 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атрибутом. Щоб отримати додаткову інформацію про ці шаблони, ви можете звернутися до цього чудового допису Бреда Вільсона: bradwilson.typepad.com/blog/2009/10/…
Даніель

2
@DotNetWise Я не впевнений, чому ти це кажеш; всі параметри рядка DisplayAttribute(включаючи підказку) можна локалізувати. Вам просто потрібно вказати ResourceType в анотації: [Display(ResourceType = typeof(PeopleResources), Prompt = "AgePrompt")]. І це все. Текст водяного знаку тепер надходить із ключової AgeGroup у ресурсі PeopleResources .
Даніель Люцці

1
Що робити, якщо ви використовуєте не ресурси .resx, а систему локалізації i18N .po?
Адаптабі

3
EditorTemplatesПапка @FrancisRodgers за замовчуванням відсутня; ви просто створюєте у своїй Views\Sharedпапці (або Views\{ControllerName}якщо ви хочете, щоб вона була специфічною для певного контролера). Потім ви розміщуєте свої шаблони .cshtml всередині цієї папки, і ви повинні бути готові до роботи.
Даніель Люцці

2
@RobertIvanc Я відредагував відповідь і скасував редагування, зроблене Ролі Бакнером, яке спричиняє проблеми, про які Ви та Тед повідомляли. Дякую.
Даніель Люцці

22

Я насправді вважаю за краще використовувати відображуване ім'я для тексту заповнювача більшу частину часу. Ось приклад використання DisplayName:

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

1
Існує спеціальна анотація даних Запит на водяний знак. І DisplayName призначений для мітки поля. Погано їх змішувати. Використовуйте правильні речі для правильних завдань. Подивіться на мою відповідь.
Mike Eshva

1
спасибі, це те, що я шукав, просте і до того, що ми можемо отримати відображуване ім'я, тоді навіщо додавати більше класів
saqibahmad

Це призведе до подвійного екрану тексту, наданого DisplayName - це не є гарним рішенням, наприклад, мови з наголосами, як французька.
марапет

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)})

І властивість у viewmodel:

[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, якщо значення Display -> Prompt не вказано?
Саша Танцев

Де ви зберігаєте свій клас WatermarkExtension, щоб потім його можна було використовувати, як ви описали? Html.WatermarkFor (model => model.AddressSuffix)
Крейг Джердінген

3

Я використовую цей спосіб із файлом ресурсу (більше не потрібно запитувати!)

@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 не є хорошим рішенням, тому що вам потрібно подбати про багато можливих tepmlata для різних випадків: рядки, числові знаки, комбіновані поля тощо. Іншим рішенням є спеціальне розширення до 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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.