Espaces réservés Html5 avec .NET MVC 3 Razor EditorPour l'extension?

92

Existe-t-il un moyen d'écrire l' espace réservé Html5 en utilisant @ Html.EditorFor, ou devrais-je simplement utiliser l'extension TextBoxFor ie

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

Ou serait-il judicieux d'écrire notre propre extension personnalisée qui peut peut-être utiliser l'attribut d'affichage 'Description' via DataAnnotations (similaire à celui-ci )?

Bien sûr, la même question s'applique également à la mise au point automatique.

cherche
la source

Réponses:

68

Vous pouvez consulter l' article suivant pour rédiger une personnalisation DataAnnotationsModelMetadataProvider.

Et voici une autre façon de procéder, plus ASP.NET MVC 3ish, impliquant la nouvelle interface IMetadataAware .

Commencez par créer un attribut personnalisé implémentant cette 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;
    }
}

Et puis décorez votre modèle avec:

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

Ensuite, définissez un contrôleur:

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

Une vue correspondante:

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

Et enfin le template de l'éditeur ( ~/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
la source
merci pour les informations (et un excellent exemple) de l'interface IMetadataAware!
seekay
4
est-ce toujours valable pour MVC3? J'ai remarqué un nouveau [Display (Prompt = "type watermark here")] dans MVC3 mais je n'ai pas pu le faire fonctionner. une idée?
smnbss
2
@smnbss Vous avez raison. Voir ma réponse pour voir comment faire Promptfonctionner.
Daniel Liuzzi
6
wow autant de travail pour faire un espace réservé? doit être quelque chose de plus simple: S
krilovich
Il y a, regardez quelques-unes des réponses ci-dessous. Le Pax en a un bon.
Termato
121

Comme le commentaire smnbss dans la réponse de Darin Dimitrov, Promptexiste exactement dans ce but, il n'est donc pas nécessaire de créer un attribut personnalisé . De la documentation:

Obtient ou définit une valeur qui sera utilisée pour définir le filigrane des invites dans l'interface utilisateur.

Pour l'utiliser, décorez simplement la propriété de votre modèle de vue comme ceci:

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

Ce texte est ensuite commodément placé dans ModelMetadata.Watermark. Hors de la boîte, le modèle par défaut dans MVC 3 ignore la Watermarkpropriété, mais le faire fonctionner est vraiment simple. Tout ce que vous avez à faire est de peaufiner le modèle de chaîne par défaut pour indiquer à MVC comment le rendre. Modifiez simplement String.cshtml, comme le fait Darin, sauf que plutôt que d'obtenir le filigrane ModelMetadata.AdditionalValues, vous l'obtenez directement à partir de ModelMetadata.Watermark:

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

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

Et c'est tout.

Comme vous pouvez le voir, la clé pour que tout fonctionne est le placeholder = ViewData.ModelMetadata.Watermarkbit.

Si vous souhaitez également activer le filigrane pour les zones de texte multilignes (zones de texte), procédez de la même manière pour 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
la source
6
@Brett Il y en a, en effet. EditorFor () est un assistant basé sur un modèle introduit dans MVC 2. À première vue, il peut sembler faire la même chose que TextBox (), mais cela vous donne le gros avantage de vous permettre de contrôler exactement comment vous voulez que votre HTML soit généré. Ma réponse est basée sur cette fonctionnalité pour «apprendre» à MVC ce qu'il faut faire avec l' Promptattribut. Pour plus d'informations sur ces modèles, vous pouvez vous référer à cet excellent article de Brad Wilson: bradwilson.typepad.com/blog/2009/10/…
Daniel Liuzzi
2
@DotNetWise Je ne sais pas pourquoi vous dites cela; tous les paramètres de chaîne de DisplayAttribute(y compris Prompt) sont localisables. Il vous suffit de spécifier le ResourceType dans votre annotation: [Display(ResourceType = typeof(PeopleResources), Prompt = "AgePrompt")]. Et c'est tout. Le texte du filigrane provient désormais de la clé AgeGroup de la ressource PeopleResources .
Daniel Liuzzi du
1
Que faire si vous n'utilisez pas de ressources .resx mais le système de localisation i18N .po?
Adaptabi
3
Le EditorTemplatesdossier @FrancisRodgers n'est pas là par défaut; vous venez de créer dans votre Views\Shareddossier (ou Views\{ControllerName}si vous voulez qu'il soit spécifique à un certain contrôleur). Vous placez ensuite vos modèles .cshtml dans ce dossier et vous devriez être prêt à partir.
Daniel Liuzzi
2
@RobertIvanc J'ai modifié la réponse et annulé la modification faite par Raleigh Buckner qui causait les problèmes que vous et Ted avez signalés. Merci.
Daniel Liuzzi
22

En fait, je préfère utiliser le nom d'affichage pour le texte d'espace réservé la plupart du temps. Voici un exemple d'utilisation de DisplayName:

  @Html.TextBoxFor(x => x.FirstName, true, null, new { @class = "form-control", placeholder = Html.DisplayNameFor(x => x.FirstName) })
Le Pax Bisonica
la source
1
Il existe une invite d'annotation de données spéciale pour un filigrane. Et DisplayName est pour l'étiquette de champ. C'est une mauvaise idée de les mélanger. Utilisez les bonnes choses pour les bonnes tâches. Regardez ma réponse.
Mike Eshva
1
merci, c'est ce que je cherchais, simple et au point où nous pouvons obtenir le nom d'affichage alors pourquoi ajouter plus de classes
saqibahmad
Cela échappera deux fois au texte fourni par DisplayName - pas une bonne solution pour les langues avec des accents comme le français par exemple.
marapet
3

J'ai écrit une classe si simple:

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

L'utilisation en tant que telle:

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

Et la propriété dans un modèle de vue:

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

Remarquez le paramètre Prompt. Dans ce cas, j'utilise des chaînes de ressources pour la localisation, mais vous pouvez utiliser uniquement des chaînes, évitez simplement le paramètre ResourceType.

Mike Eshva
la source
Juste décompilé la méthode DisplayNameFor et créé un analogue pour le filigrane.
Mike Eshva
Salut, pouvez-vous s'il vous plaît changer votre méthode MvcHtmlString WatermarkFor () pour utiliser la valeur d'attribut DisplayName si Display -> Prompt value n'est pas spécifié?
Sasa Tancev
Où enregistrez-vous votre classe WatermarkExtension afin qu'elle puisse ensuite être utilisée comme vous l'avez décrit? Html.WatermarkFor (model => model.AddressSuffix)
Craig Gjerdingen
3

J'utilise de cette façon avec le fichier de ressources (je n'ai plus besoin d'invite!)

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

Voici une solution que j'ai faite en utilisant les idées ci-dessus qui peuvent être utilisées pour TextBoxFor et 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
la source
0

Je pense que créer un EditorTemplate personnalisé n'est pas une bonne solution, car vous devez vous soucier de nombreux tepmlates possibles pour différents cas: chaînes, numsers, comboboxes, etc. Une autre solution est une extension personnalisée vers HtmlHelper.

Modèle:

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

Extension d'assistance 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);

}

Une vue correspondante:

@Html.BsEditorFor(x => x.Title)
Sel
la source