Html5 Placeholders com extensão .NET MVC 3 Razor EditorFor?

92

Existe uma maneira de escrever o marcador de posição Html5 usando @ Html.EditorFor, ou devo apenas usar a extensão TextBoxFor, ou seja,

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

Ou faria sentido escrever nossa própria extensão personalizada que talvez possa usar o atributo de exibição 'Descrição' por meio de DataAnnotations (semelhante a este )?

Claro, então a mesma pergunta se aplica ao 'foco automático' também.

seeay
fonte

Respostas:

68

Você pode dar uma olhada no seguinte artigo para escrever umDataAnnotationsModelMetadataProvider .

E aqui está outra maneira mais ASP.NET MVC 3 de proceder envolvendo a interface IMetadataAware recém-introduzida .

Comece criando um atributo personalizado implementando esta interface:

public class PlaceHolderAttribute : Attribute, IMetadataAware
{
    private readonly string _placeholder;
    public PlaceHolderAttribute(string placeholder)
    {
        _placeholder = placeholder;
    }

    public void OnMetadataCreated(ModelMetadata metadata)
    {
        metadata.AdditionalValues["placeholder"] = _placeholder;
    }
}

E então decore seu modelo com ele:

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

Em seguida, defina um controlador:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View(new MyViewModel());
    }
}

Uma visão correspondente:

@model MyViewModel
@using (Html.BeginForm())
{
    @Html.EditorFor(x => x.Title)
    <input type="submit" value="OK" />
}

E, finalmente, o modelo de editor ( ~/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>
Darin Dimitrov
fonte
obrigado pela informação (e um ótimo exemplo) da interface IMetadataAware!
searchay
4
isso ainda é válido para MVC3? Notei um novo [Display (Prompt = "digite marca d'água aqui")] no MVC3, mas não consegui fazer funcionar. qualquer ideia?
smnbss
2
@smnbss Você está correto. Veja minha resposta para ver como fazer Promptfuncionar.
Daniel Liuzzi
6
uau tanto trabalho para fazer um espaço reservado? tem que ser algo mais simples: S
Krilovich
Sim, veja algumas das respostas abaixo. O Pax tem um bom.
Termato
121

Como comentários smnbss na resposta de Darin Dimitrov, Promptexiste exatamente para esse propósito, portanto, não há necessidade de criar um atributo personalizado . Da documentação:

Obtém ou define um valor que será usado para definir a marca d'água para prompts na IU.

Para usá-lo, basta decorar a propriedade do seu modelo de visualização assim:

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

Este texto é então convenientemente colocado em ModelMetadata.Watermark. Pronto para usar, o modelo padrão em MVC 3 ignora a Watermarkpropriedade, mas fazê-la funcionar é realmente simples. Tudo que você precisa fazer é ajustar o modelo de string padrão para dizer ao MVC como renderizá-lo. Basta editar String.cshtml, como Darin faz, exceto que, em vez de obter a marca d'água de ModelMetadata.AdditionalValues, você a obtém diretamente deModelMetadata.Watermark :

~ / Views / Shared / EditorTemplates / String.cshtml:

@Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { @class = "text-box single-line", placeholder = ViewData.ModelMetadata.Watermark })

E é isso.

Como você pode ver, a chave para fazer tudo funcionar é o placeholder = ViewData.ModelMetadata.Watermarkbit.

Se você também deseja habilitar a marca d'água para caixas de texto com várias linhas (áreas de texto), faça o mesmo para 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 })
Daniel Liuzzi
fonte
6
@Brett Sim, sim. EditorFor () é uma templated ajudante introduzido no MVC 2. À primeira vista pode parecer a fazer a mesma coisa que TextBox (), mas dá-lhe a grande vantagem de permitir que você controle exatamente como você deseja gerado seu HTML. Minha resposta é baseada neste recurso para "ensinar" ao MVC o que fazer com o Promptatributo. Para obter mais informações sobre esses modelos, você pode consultar esta excelente postagem de Brad Wilson: bradwilson.typepad.com/blog/2009/10/…
Daniel Liuzzi
2
@DotNetWise Não sei por que você diz isso; todos os parâmetros de string de DisplayAttribute(incluindo Prompt) são localizáveis. Você só precisa especificar o ResourceType em sua anotação: [Display(ResourceType = typeof(PeopleResources), Prompt = "AgePrompt")]. E é isso. O texto da marca d'água agora vem do AgeGroup chave no recurso PeopleResources .
Daniel Liuzzi
1
E se você não estiver usando recursos .resx, mas o sistema de localização i18N .po?
Adaptabi
3
A EditorTemplatespasta @FrancisRodgers não está lá por padrão; você acabou de criar na sua Views\Sharedpasta (ou Views\{ControllerName}se quiser que seja específico para um determinado controlador). Em seguida, coloque seus modelos .cshtml dentro desta pasta e estará pronto para prosseguir.
Daniel Liuzzi
2
@RobertIvanc Editei a resposta e reverti a edição feita por Raleigh Buckner que causou os problemas que você e Ted relataram. Obrigado.
Daniel Liuzzi
22

Na verdade, prefiro usar o nome de exibição para o texto do espaço reservado na maioria das vezes. Aqui está um exemplo de uso do DisplayName:

  @Html.TextBoxFor(x => x.FirstName, true, null, new { @class = "form-control", placeholder = Html.DisplayNameFor(x => x.FirstName) })
A Pax Bisonica
fonte
1
Há um prompt de anotação de dados especial para uma marca d'água. E DisplayName é para rótulo de campo. É uma má ideia misturá-los. Use as coisas certas para as tarefas certas. Veja minha resposta.
Mike Eshva
1
Obrigado, é isso que eu estava procurando, simples e
direto
Isso irá escapar duas vezes do texto fornecido por DisplayName - não é uma boa solução para idiomas de exemplo com acentos como francês.
marapet
3

Eu escrevi uma aula tão simples:

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

O uso como tal:

@Html.TextBoxFor(model => model.AddressSuffix, new {placeholder = Html.WatermarkFor(model => model.AddressSuffix)})

E propriedade em um modelo de vista:

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

Observe o parâmetro Prompt. Neste caso, eu uso strings de recursos para localização, mas você pode usar apenas strings, apenas evite o parâmetro ResourceType.

Mike Eshva
fonte
Apenas descompilou o método DisplayNameFor e criou um análogo para a marca d'água.
Mike Eshva
Olá, você pode alterar seu método MvcHtmlString WatermarkFor () para usar o valor do atributo DisplayName se Display -> Valor do prompt não for especificado?
Sasa Tancev
Onde você salva sua classe WatermarkExtension para que possa ser usada como você descreveu? Html.WatermarkFor (model => model.AddressSuffix)
Craig Gjerdingen de
3

Eu uso assim com arquivo de recursos (não preciso mais do Prompt!)

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

Aqui está uma solução que fiz usando as idéias acima que podem ser usadas para TextBoxFor e 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));
    }
}
Vladimir
fonte
0

Acho que criar um EditorTemplate personalizado não é uma boa solução, porque você precisa se preocupar com muitos tepmlates possíveis para diferentes casos: strings, numsers, comboboxes e assim por diante. Outra solução é a extensão personalizada para HtmlHelper.

Modelo:

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

Extensão auxiliar 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);

}

Uma visão correspondente:

@Html.BsEditorFor(x => x.Title)
Sel
fonte