È possibile creare un metodo @helper generico con Razor?


88

Sto cercando di scrivere un helper in Razor che assomigli al seguente:

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

Sfortunatamente, il parser pensa che <Tsia l'inizio di un elemento HTML e io finisco con un errore di sintassi. È possibile creare un helper con Razor che è un metodo generico? In caso affermativo, qual è la sintassi?


Ancora non risolto nell'attuale versione di MVC 4. :(
Alex Dresko

1
In che modo questo non è ancora stato risolto in VS2012?
Alex Dresko

7
Santo cielo, non vedo l'ora che venga aggiunto questo; Spero che questo sia da qualche parte " implementalo ieri " nell'elenco delle priorità. Parzialmente fuori tema, ma accanto a questo, mi piacerebbe vedere che le classi generate lo sono static, a meno che i dettagli di implementazione non lo proibiscano; La ragione è che si potrebbero usare degli helper di estensione generici :@helper Foo<T>(this T o) where T : IBar { }
Dan Lugg

Risposte:


52

No, non è possibile. Puoi invece scrivere un normale helper HTML.

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

e poi:

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

o se stai prendendo di mira il modello come primo argomento generico:

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

che ti permetterà di invocarlo in questo modo (supponendo ovviamente che la tua vista sia fortemente tipizzata, ma questo è un presupposto sicuro perché tutte le viste dovrebbero essere comunque fortemente digitate :-)):

@Html.DoSomething(x => x.SomeProperty)

10
Si spera che questo sia qualcosa che aggiungono a una futura versione degli helper Razor. La leggibilità di un helper tradizionale è molto inferiore alla sintassi @helper.
mkedobbs

2
Sì, d'accordo. Tornare al vecchio metodo non solo fa schifo, ma divide arbitrariamente i tuoi aiutanti!
George R

126

Questo è possibile ottenere all'interno di un file helper con la @functionssintassi, ma se vuoi la leggibilità in stile rasoio a cui ti riferisci, dovrai anche chiamare un assistente regolare per adattare l'HTML e finire.

Nota che le funzioni in un file Helper sono statiche, quindi dovresti comunque passare l'istanza HtmlHelper dalla pagina se intendi utilizzare i suoi metodi.

ad esempio 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>
}
...

Questo è perfetto. Grazie.
Ken Smith

NON devi rendere il metodo statico, e quindi NON devi
nemmeno

12
@ Sheepy, è vero solo a metà. Hai ragione, puoi renderli non statici, ma ottieni solo System.Web.WebPages.Html.HtmlHelperpiuttosto che System.Web.Mvc.HtmlHelper. C'è un'eccellente possibilità che la WebPagesversione non sia adatta a te, poiché la maggior parte dei metodi di estensione sono scritti contro System.Web.Mvc.HtmlHelper. Inoltre, non è presente alcuna Urlproprietà e UrlHelperrichiede una RequestContextnon disponibile nella WebPagesversione. Tutto sommato probabilmente dovrai passare in Mvc HtmlHelper.
Kirk Woll

1
l'helper deve far parte della cartella App_Code ??
Vishal Sharma

1
Sì, questo file deve essere inserito in {MyMvcProject}\App_Code`. It doesn't work as advertised when you place it elsewhere. The error *Cannot access non-static method 'TheThingToDo' in static context* disappears when you move MyHelper.cshtml` in App_Code. DoSomethingdovrebbe essere statico, in modo che tu possa chiamare @MyHelper.DoSomething(..)nella tua vista. Se lo rendi non statico, devi prima creare un'istanza di MyHelper.
Grilse

3

In tutti i casi TModelsarà lo stesso (il modello dichiarato per la vista) e, nel mio caso, TValuesarebbe stato lo stesso, quindi sono stato in grado di dichiarare il tipo di argomento 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>
}

Se i campi del tuo modello sono tutti string, puoi sostituirli MyClasscon string.

Potrebbe non essere male definire due o tre helper con il TValuedefinito, ma se ne hai altri che genererebbero del codice brutto, non ho davvero trovato una buona soluzione. Ho provato a racchiudere il @helperda una funzione che ho inserito nel @functions {}blocco, ma non sono mai riuscito a farlo funzionare su quel percorso.


Un po 'ovvio quando ci pensi - se è TModelprobabilmente lo sai in anticipo.
ta.speot.is

0

se il tuo problema principale è ottenere il valore dell'attributo name per il binding usando l'espressione lambda sembra come il @Html.TextBoxFor(x => x.MyPoperty), e se il tuo componente ha tag html molto complessi e dovrebbe essere implementato su razor helper, allora perché non creare semplicemente un metodo di estensione HtmlHelper<TModel>per risolvere il nome associazione:

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

il tuo aiuto rasoio dovrebbe essere come al solito:

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

allora qui puoi usarlo

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

Non è questo a cosa serve @ Html.IdFor (...)?
Alex Dresko

Sì, puoi farlo @Htm.IdForma hai bisogno di un processo aggiuntivo per convertirlo in string ( .ToHtmlString()) dove l'helper richiede la stringa
ktutnik
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.