Razorでジェネリック@helperメソッドを作成することは可能ですか?


88

私は次のようなヘルパーでヘルパーを書こうとしています:

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

残念ながら、パーサーはそれ<TがHTML要素の始まりであると考え、構文エラーが発生します。ジェネリックメソッドであるRazorでヘルパーを作成することは可能ですか?もしそうなら、構文は何ですか?


現在のMVC 4リリースではまだ修正されていません。:(
Alex Dresko

1
これはVS2012でまだ修正されていないのですか?
Alex Dresko

7
これが追加されるのが待ちきれません。これが優先リストの「昨日実装する」の周りのどこかにあるといいのですが。部分的にトピックから外れていますが、これとともにstatic、実装の詳細で禁止されていない限り、生成されたクラスがであることを確認したいと思います。であることの理由は、1を使用することができている一般的な拡張ヘルパーを@helper Foo<T>(this T o) where T : IBar { }
ダンLugg

回答:


52

いいえ、できません。代わりに、通常のHTMLヘルパーを作成できます。

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

その後:

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

または、最初のジェネリック引数としてモデルをターゲットにしている場合:

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

これにより、次のように呼び出すことができます(もちろん、ビューが強く型付けされていると仮定しますが、すべてのビューはとにかく強く型付けされる必要があるため、これは安全な仮定です:-)):

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

10
うまくいけば、これはRazorヘルパーの将来のバージョンに追加されるものです。従来のヘルパーの読みやすさは、@ helper構文よりもはるかに低くなります。
mkedobbs '22年

2
そうだね。以前の方法に戻すと、面倒なだけでなく、ヘルパーが任意に分割されます。
ジョージR

126

これ@functions構文を使用してヘルパーファイル内で実行できますが、かみそりスタイルの可読性が必要な場合は、参照しているHTMLに合わせてフィニッシュするために通常のヘルパーを呼び出す必要があります。

ヘルパーファイルの関数は静的であるため、そのメソッドを使用する場合は、ページからHtmlHelperインスタンスを渡す必要があることに注意してください。

例: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>
}
...

これは完璧です。ありがとう。
ケン・スミス

メソッドを静的にする必要はありません。したがって、Html / Url / Modelなどを渡す必要もありません
Sheepy

12
@シープ、それは真実の半分だけです。あなたは正しいです、あなたはそれらを非静的にすることができますが、あなたSystem.Web.WebPages.Html.HtmlHelperはよりもむしろ得るだけですSystem.Web.Mvc.HtmlHelperWebPagesほとんどの拡張メソッドはに対して作成されているため、バージョンが適切ではない可能性が非常に高くなりますSystem.Web.Mvc.HtmlHelper。さらに、Urlプロパティはなく、バージョンで使用できないUrlHelperが必要です。全体として、おそらくを渡す必要があります。RequestContextWebPagesMvc HtmlHelper
カークウォル

1
ヘルパーはApp_Codeフォルダーの一部である必要がありますか?
Vishal Sharma 2013

1
はい、このファイルは{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`のに配置する必要がありますApp_Code。あなたはあなたのビューでDoSomething呼び出すことができるように、静的でなければなりません@MyHelper.DoSomething(..)。非静的にする場合は、MyHelper最初のインスタンスを作成する必要があります。
Grilse、2015年

3

すべての場合でTModel同じになります(ビューに対して宣言されたモデル)。私の場合、TValue同じになるため、式の引数の型を宣言することができました。

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

お使いのモデルのフィールドがすべてであればstring、あなたは置き換えることができMyClassstring

2つまたは3つのヘルパーをTValue定義済みで定義することは悪くないかもしれませんが、醜いコードを生成するヘルパーがそれ以上ある場合、私は本当に良い解決策を見つけられませんでした。ブロック@helper内に配置した関数からをラップしてみましたが、@functions {}そのパスを処理することができませんでした。


あなたがそれについて考えるとき、一種の明白なものです-もしそうなら、TModelあなたはおそらく事前にそれを知っています。
ta.speot.is 2017年

0

主な問題がラムダ式を使用してバインドするための名前属性値を取得すること@Html.TextBoxFor(x => x.MyPoperty)である場合、およびコンポーネントに非常に複雑なhtmlタグがあり、かみそりヘルパーに実装する必要がある場合は、の拡張メソッドを作成してHtmlHelper<TModel>、バインディング名:

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

あなたのかみそりヘルパーはいつものようでなければなりません:

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

次に、あなたはそれを使うことができます

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

これは@ Html.IdFor(...)の目的ではありませんか?
Alex Dresko 2013年

はい、できますが、ヘルパーが文字列を必要@Htm.IdForとする文字列(.ToHtmlString())に変換するための追加のプロセスが必要です
ktutnik
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.