webentwicklung-frage-antwort-db.com.de

Kann man mit Razor eine generische @helper-Methode erstellen?

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

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

Leider denkt der Parser, dass <T der Anfang eines HTML-Elements ist und ich am Ende einen Syntaxfehler habe. Kann man mit Razor einen Helfer erstellen, der eine generische Methode ist? Wenn ja, wie lautet die Syntax?

83
mkedobbs

Nein das ist nicht möglich. Sie könnten 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
{
    ...
}

dies erlaubt es Ihnen, es so aufzurufen (vorausgesetzt natürlich, dass Ihre Ansicht stark typisiert ist, aber dies ist eine sichere Annahme, da alle Ansichten sowieso stark typisiert sein sollten :-))

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

Dies ist innerhalb einer Hilfsdatei mit der @functions-Syntax zu erreichen. Wenn Sie jedoch die Lesbarkeit von Rasierern verwenden möchten, auf die Sie sich beziehen, müssen Sie auch einen regulären Helfer aufrufen, um die HTML-Anpassung und das Fertigstellen durchzuführen.

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

z.B. . Ansichten\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

In allen Fällen ist TModel gleich (das für die Ansicht deklarierte Modell), und in meinem Fall würde TValue gleich sein. Daher konnte ich den Typ des Ausdrucksargumentes deklarieren:

@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 Ihre Modellfelder alle string sind, können Sie MyClass durch string ersetzen.

Es ist vielleicht nicht schlimm, zwei oder drei Helfer mit der definierten TValue zu definieren, aber wenn Sie weitere haben, die hässlichen Code erzeugen, habe ich keine wirklich gute Lösung gefunden. Ich habe versucht, den @helper von einer Funktion einzuwickeln, die ich in den @functions {}-Block eingefügt habe, aber ich habe es nie geschafft, auf diesem Pfad zu arbeiten.

2
bradlis7

wenn Ihr Hauptproblem darin besteht, den name - Attributwert für die Bindung mit einem Lambda-Ausdruck zu erhalten, scheint die Funktion @Html.TextBoxFor(x => x.MyPoperty) zu sein. Wenn Ihre Komponente sehr komplexe HTML-Tags enthält und auf dem Rasierer-Helfer implementiert werden sollte, erstellen Sie eine Erweiterung Methode von HtmlHelper<TModel> zum Auflösen des Bindungsnamens:

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 Rasierhelfer sollte wie üblich sein:

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

dann kannst du es hier benutzen

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