Apakah mungkin membuat metode @helper generik dengan Razor?

90

Saya mencoba menulis pembantu di Razor yang terlihat seperti berikut:

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

Sayangnya, pengurai berpikir itu <Tadalah awal dari elemen HTML dan saya berakhir dengan kesalahan sintaksis. Apakah mungkin membuat helper dengan Razor yang merupakan metode umum? Jika ya, apa sintaksnya?

mkedobbs
sumber
Masih belum diperbaiki dalam rilis MVC 4 saat ini. :(
Alex Dresko
1
Bagaimana ini masih belum diperbaiki di VS2012?
Alex Dresko
7
Ya ampun, saya tidak sabar menunggu ini ditambahkan; Saya berharap ini ada di suatu tempat di sekitar " mengimplementasikannya kemarin " di daftar prioritas. Sebagian di luar topik, tetapi di samping ini, saya ingin melihat bahwa kelas yang dihasilkan adalah static, kecuali jika detail implementasi melarangnya; Alasannya, adalah seseorang dapat menggunakan pembantu ekstensi generik :@helper Foo<T>(this T o) where T : IBar { }
Dan Lugg

Jawaban:

52

Tidak, saat ini tidak memungkinkan. Sebagai gantinya, Anda bisa menulis pembantu HTML biasa.

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

lalu:

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

atau jika Anda menargetkan model sebagai argumen umum pertama:

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

yang memungkinkan Anda untuk memanggilnya seperti ini (tentu saja dengan asumsi bahwa pandangan Anda diketik dengan kuat, tetapi itu adalah asumsi yang aman karena semua tampilan harus diketik dengan kuat :-)):

@Html.DoSomething(x => x.SomeProperty)
Darin Dimitrov
sumber
10
Mudah-mudahan ini adalah sesuatu yang mereka tambahkan ke versi pembantu Razor di masa mendatang. Keterbacaan helper tradisional jauh lebih rendah daripada sintaks @helper.
mkedobbs
2
Ya setuju. Kembali ke metode lama tidak hanya menyebalkan, tetapi juga membagi pembantu Anda secara sewenang-wenang!
George R
127

Hal ini dimungkinkan untuk dicapai di dalam file helper dengan @functionssintaks tetapi jika Anda menginginkan keterbacaan gaya pisau cukur yang Anda maksud, Anda juga perlu memanggil helper biasa untuk melakukan fit dan menyelesaikan HTML.

Perhatikan bahwa fungsi dalam file Helper bersifat statis sehingga Anda masih perlu meneruskan instance HtmlHelper dari halaman jika Anda bermaksud menggunakan metodenya.

mis. 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
sumber
Ini sempurna. Terima kasih.
Ken Smith
Anda TIDAK harus membuat metode ini statis, dan dengan demikian Anda juga TIDAK perlu meneruskan Html / Url / Model dll
Sheepy
12
@Sheap, itu hanya setengah benar. Anda benar, Anda dapat membuatnya non-statis, tetapi Anda hanya mendapatkan System.Web.WebPages.Html.HtmlHelperdaripada System.Web.Mvc.HtmlHelper. Ada kemungkinan besar bahwa WebPagesversi tersebut tidak akan cocok untuk Anda, karena sebagian besar metode ekstensi tidak sesuai System.Web.Mvc.HtmlHelper. Selain itu, tidak ada Urlproperti, dan UrlHelpermembutuhkan RequestContextyang tidak tersedia dalam WebPagesversi. Semua di dalam semua Anda mungkin harus lulus di Mvc HtmlHelper.
Kirk Woll
1
helper harus menjadi bagian dari App_Code Folder ??
Vishal Sharma
1
Ya, file ini harus ditempatkan di {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` ke App_Code. DoSomethingharus statis, sehingga Anda dapat memanggil @MyHelper.DoSomething(..)dalam tampilan Anda. Jika Anda membuatnya non-statis, Anda harus membuat instance MyHelperterlebih dahulu.
Grilse
3

Dalam semua kasus, TModelakan sama (model dideklarasikan untuk tampilan), dan dalam kasus saya, TValuetadinya akan sama, jadi saya bisa mendeklarasikan tipe argumen Ekspresi:

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

Jika bidang model Anda semuanya string, maka Anda dapat menggantinya MyClassdenganstring .

Mungkin tidak buruk untuk mendefinisikan dua atau tiga pembantu dengan yang TValueditentukan, tetapi jika Anda memiliki lebih banyak yang akan menghasilkan beberapa kode jelek, saya tidak benar-benar menemukan solusi yang baik. Saya mencoba membungkus @helperdari fungsi yang saya masukkan ke dalam @functions {}blok, tetapi saya tidak pernah berhasil melakukannya di jalur itu.

bradlis7
sumber
Agak jelas ketika Anda memikirkannya - jika TModelAnda mungkin mengetahuinya sebelumnya.
ta.speot. Adalah
0

Jika masalah utama Anda adalah mendapatkan nilai atribut nama untuk mengikat menggunakan ekspresi lambda sepertinya @Html.TextBoxFor(x => x.MyPoperty), dan jika komponen Anda memiliki tag html yang sangat kompleks dan harus diterapkan pada pisau cukur, mengapa tidak membuat metode ekstensi HtmlHelper<TModel>untuk menyelesaikan nama yang mengikat:

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

pisau cukur Anda harus seperti biasa:

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

lalu di sini Anda bisa menggunakannya

@TheHelper.MyComponent(Html.GetNameForBinding(x => x.MyProperty))
ktutnik
sumber
Bukankah ini tujuan @ Html.IdFor (...)?
Alex Dresko
Ya, Anda dapat melakukannya @Htm.IdFortetapi memerlukan proses ekstra untuk mengubahnya menjadi string ( .ToHtmlString()) di mana helper memerlukan string
ktutnik