web-dev-qa-db-fra.com

Est-il possible de créer une méthode @helper générique avec Razor?

J'essaie d'écrire une aide dans Razor qui ressemble à ceci:

@helper DoSomething<T, U>(Expression<Func<T, U>> expr) where T : class

Malheureusement, l'analyseur pense que <T est le début d'un élément HTML et je me retrouve avec une erreur de syntaxe. Est-il possible de créer une aide avec Razor qui soit une méthode générique? Si oui, quelle est la syntaxe?

83
mkedobbs

Non, ce n'est pas possible. Vous pouvez écrire un assistant HTML normal à la place.

public static MvcHtmlString DoSomething<T, U>(
    this HtmlHelper htmlHelper, 
    Expression<Func<T, U>> expr
) where T : class
{
    ...
}

et alors:

@(Html.DoSomething<SomeModel, string>(x => x.SomeProperty))

ou si vous ciblez le modèle en tant que premier argument générique:

public static MvcHtmlString DoSomething<TModel, TProperty>(
    this HtmlHelper<TModel> htmlHelper, 
    Expression<Func<TModel, TProperty>> expr
) where T : class
{
    ...
}

ce qui vous permettra de l'invoquer comme ceci (en supposant bien sûr que votre vue soit fortement typée, mais c'est une hypothèse sûre, car toutes les vues doivent être fortement typées de toute façon :-)):

@Html.DoSomething(x => x.SomeProperty)
48
Darin Dimitrov

Ceci est possible à réaliser à l'intérieur d'un fichier d'assistance avec la syntaxe @functions, mais si vous souhaitez une lisibilité de style rasoir, vous aurez également besoin d'appeler une aide régulière pour effectuer l'ajustement et la finition du code HTML.

Notez que les fonctions d'un fichier d'assistance sont statiques, vous devez donc toujours transmettre l'occurrence HtmlHelper à partir de la page si vous souhaitez utiliser ses méthodes.

par exemple . Views\MyView.cshtml:

@MyHelper.DoSomething(Html, m=>m.Property1)
@MyHelper.DoSomething(Html, m=>m.Property2)
@MyHelper.DoSomething(Html, m=>m.Property3)

App_Code\MyHelper.cshtml:

@using System.Web.Mvc;
@using System.Web.Mvc.Html;
@using System.Linq.Expressions;
@functions
{
    public static HelperResult DoSomething<TModel, TItem>(HtmlHelper<TModel> html, Expression<Func<TModel, TItem>> expr)
    {
        return TheThingToDo(html.LabelFor(expr), html.EditorFor(expr), html.ValidationMessageFor(expr));
    }
}
@helper TheThingToDo(MvcHtmlString label, MvcHtmlString textbox, MvcHtmlString validationMessage)
{
    <p>
        @label
        <br />
        @textbox
        @validationMessage
    </p>
}
...
122
chrismilleruk

Dans tous les cas, la TModel sera la même (le modèle déclaré pour la vue), et dans mon cas, la TValue serait identique, j'ai donc pu déclarer le type d'argument Expression:

@helper FormRow(Expression<Func<MyViewModel, MyClass>> expression) {
  <div class="form-group">
    @(Html.LabelFor(expression, new { @class = "control-label col-sm-6 text-right" }))
    <div class="col-sm-6">
      @Html.EnumDropDownListFor(expression, new { @class = "form-control" })
    </div>
    @Html.ValidationMessageFor(expression)
  </div>
}

Si vos champs de modèle sont tous string, vous pouvez remplacer MyClass par string.

Il n’est peut-être pas mauvais de définir deux ou trois assistants avec la variable TValue définie, mais si vous en avez plus qui généreraient un code laid, je n’aurais pas vraiment trouvé de bonne solution. J'ai essayé d'encapsuler le @helper à partir d'une fonction que j'ai insérée dans le bloc @functions {}, mais je ne l'ai jamais obtenu pour fonctionner dans cette voie.

2
bradlis7

si votre problème principal est d'obtenir la valeur d'attributnompour la liaison à l'aide de l'expression lambda ressemble à la fonction @Html.TextBoxFor(x => x.MyPoperty), et si votre composant possède des balises HTML très complexes et doit être implémenté sur razor helper, pourquoi ne pas créer une extension Méthode de HtmlHelper<TModel> pour résoudre le nom de la liaison:

namespace System.Web.Mvc
{
    public static class MyHelpers
    {
        public static string GetNameForBinding<TModel, TProperty>
           (this HtmlHelper<TModel> model, 
            Expression<Func<TModel, TProperty>> property)
        {
            return ExpressionHelper.GetExpressionText(property);
        }
    }
}

votre assistant de rasoir devrait être comme d'habitude:

@helper MyComponent(string name)
{
    <input name="@name" type="text"/>
}

alors ici vous pouvez l'utiliser

@TheHelper.MyComponent(Html.GetNameForBinding(x => x.MyProperty))
0
ktutnik