Ist es möglich, mit Razor eine generische @ helper-Methode zu erstellen?

88

Ich versuche, in Razor einen Helfer zu schreiben, der wie folgt aussieht:

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

Leider denkt der Parser, dass dies <Tder Anfang eines HTML-Elements ist und ich am Ende einen Syntaxfehler habe. Ist es möglich, mit Razor einen Helfer zu erstellen, der eine generische Methode ist? Wenn ja, wie lautet die Syntax?

mkedobbs
quelle
In der aktuellen MVC 4-Version immer noch nicht behoben. :(
Alex Dresko
1
Wie ist das in VS2012 noch nicht behoben?
Alex Dresko
7
Meine Güte, ich kann es kaum erwarten, dass dies hinzugefügt wird. Ich hoffe, dass dies irgendwo auf der Prioritätenliste um " gestern implementieren " steht. Teilweise nicht zum Thema gehörend, aber daneben würde ich gerne sehen, dass die generierten Klassen sind static, es sei denn, Implementierungsdetails verbieten dies. Grund dafür ist, dass man generische Erweiterungshelfer verwenden könnte :@helper Foo<T>(this T o) where T : IBar { }
Dan Lugg

Antworten:

52

Nein das ist nicht möglich. Sie können stattdessen einen normalen HTML-Helfer schreiben.

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

und dann:

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

oder wenn Sie das Modell als erstes generisches Argument anvisieren:

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

Damit können Sie es so aufrufen (vorausgesetzt natürlich, dass Ihre Ansicht stark typisiert ist, aber das ist eine sichere Annahme, da alle Ansichten sowieso stark typisiert sein sollten :-)):

@Html.DoSomething(x => x.SomeProperty)
Darin Dimitrov
quelle
10
Hoffentlich ist dies etwas, das sie einer zukünftigen Version von Razor-Helfern hinzufügen. Die Lesbarkeit eines herkömmlichen Helfers ist viel geringer als die @ helper-Syntax.
mkedobbs
2
Ja stimmte zu. Die Rückkehr zur älteren Methode ist nicht nur zum Kotzen, sondern teilt Ihre Helfer willkürlich auf!
George R
126

Dies ist in einer Hilfedatei mit der @functionsSyntax möglich. Wenn Sie jedoch die Lesbarkeit im Rasiermesser-Stil wünschen, auf die Sie sich beziehen, müssen Sie auch einen regulären Helfer aufrufen, um die HTML-Anpassung und -Endung durchzuführen.

Beachten Sie, dass die Funktionen in einer Hilfedatei statisch sind, sodass Sie die HtmlHelper-Instanz weiterhin von der Seite übergeben müssen, wenn Sie ihre Methoden verwenden möchten.

zB 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>
}
...
chrismilleruk
quelle
Dies ist perfekt. Vielen Dank.
Ken Smith
Sie müssen die Methode NICHT statisch machen, und daher müssen Sie auch NICHT Ihre HTML / URL / Modell usw.
Sheepy
12
@ Sheepy, das ist nur halb wahr. Sie haben Recht, Sie können sie nicht statisch machen, aber Sie bekommen nur System.Web.WebPages.Html.HtmlHelperstatt System.Web.Mvc.HtmlHelper. Es besteht eine hervorragende Chance, dass die WebPagesVersion nicht für Sie geeignet ist, da die meisten Erweiterungsmethoden dagegen geschrieben sind System.Web.Mvc.HtmlHelper. Darüber hinaus gibt es keine UrlEigenschaft und UrlHelpererfordert eine, RequestContextdie in der WebPagesVersion nicht verfügbar ist . Alles in allem müssen Sie wahrscheinlich in der übergeben Mvc HtmlHelper.
Kirk Woll
1
Der Helfer muss Teil des App_Code-Ordners sein.
Vishal Sharma
1
Ja, diese Datei muss 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 abgelegt werden App_Code. DoSomethingsollte statisch sein, damit Sie @MyHelper.DoSomething(..)in Ihrer Ansicht aufrufen können. Wenn Sie es nicht statisch machen, müssen Sie MyHelperzuerst eine Instanz von erstellen .
Grilse
3

In allen Fällen TModelist derselbe derselbe (das für die Ansicht deklarierte Modell), und in meinem Fall TValuesollte derselbe derselbe sein, sodass ich den Argumenttyp des Ausdrucks deklarieren konnte:

@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>
}

Wenn Ihr Modell Felder alle sind string, dann können Sie ersetzen MyClassmit string.

Es mag nicht schlecht sein, zwei oder drei Helfer mit den TValuedefinierten zu definieren, aber wenn Sie mehr haben, die hässlichen Code erzeugen würden, habe ich keine wirklich gute Lösung gefunden. Ich habe versucht, das @helpervon einer Funktion zu verpacken, die ich in den @functions {}Block eingefügt habe, aber ich habe es nie dazu gebracht, diesen Pfad zu bearbeiten.

bradlis7
quelle
Ein bisschen offensichtlich, wenn Sie darüber nachdenken - wenn es so ist TModel, wissen Sie es wahrscheinlich im Voraus.
ta.speot.is
0

wenn Ihr Hauptproblem ist , erhalten Namen scheint Lambda - Ausdruck Attributwert für die Bindung verwendet wie das @Html.TextBoxFor(x => x.MyPoperty), und wenn Ihre Komponente sehr komplexe HTML - Tags mit und soll auf gestochen Helfern umgesetzt werden, warum dann nicht nur eine Erweiterung Methode erstellen HtmlHelper<TModel>lösen die verbindlicher Name:

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

Ihr Rasiererhelfer sollte wie gewohnt sein:

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

dann können Sie es hier verwenden

@TheHelper.MyComponent(Html.GetNameForBinding(x => x.MyProperty))
ktutnik
quelle
Ist das nicht was @ Html.IdFor (...) ist?
Alex Dresko
Ja, Sie können damit @Htm.IdFor.ToHtmlString()
umgehen