Заполнители HTML5 с расширением .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 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>
Дарин Димитров
источник
спасибо за информацию (и отличный пример) интерфейса IMetadataAware!
seekay
4
это все еще действительно для MVC3? Я заметил новый [Display (Prompt = "type watermark here")] в MVC3, но не смог заставить его работать. любая идея?
smnbss
2
@smnbss Вы правы. См. Мой ответ, чтобы узнать, как заставить Promptработать.
Daniel Liuzzi
6
вау столько работы по заполнению? должно быть что-то попроще: S
krilovich 08
Есть, посмотрите на некоторые ответы ниже. У 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(включая Prompt) можно локализовать. Вам просто нужно указать ResourceType в аннотации: [Display(ResourceType = typeof(PeopleResources), Prompt = "AgePrompt")]. Вот и все. Текст водяного знака теперь берется из ключевой AgeGroup в ресурсе PeopleResources .
Даниэль Лиуцци,
1
Что делать, если вы используете не ресурсы .resx, а систему локализации i18N .po?
Adaptabi
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 - для метки поля. Смешивать их - плохая идея. Используйте правильные вещи для правильных задач. Посмотри на мой ответ.
Майк Эшва
1
спасибо, это то, что я искал, просто и до такой степени, что мы можем получить отображаемое имя, тогда зачем добавлять больше классов
saqibahmad
Это приведет к двойному экранированию текста, предоставленного 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 и сделал аналог водяного знака.
Майк Эшва
Привет, не могли бы вы изменить свой метод MvcHtmlString WatermarkFor (), чтобы использовать значение атрибута DisplayName, если значение Display -> Prompt не указано?
Саша Танцев,
Где вы сохраняете свой класс WatermarkExtension, чтобы его можно было использовать, как вы описали? Html.WatermarkFor (model => model.AddressSuffix)
Craig Gjerdingen
3

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

@Html.TextBoxFor(m => m.Name, new 
{
     @class = "form-control",
     placeholder = @Html.DisplayName(@Resource.PleaseTypeName),
     autofocus = "autofocus",
     required = "required"
})
xicooc
источник
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 не является хорошим решением, потому что вам нужно позаботиться о многих возможных tepmlates для разных случаев: строки, числа, поля со списком и так далее. Другое решение - индивидуальное расширение 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)
Sel
источник