Próbuję napisać pomocnika w Razor, który wygląda następująco:
@helper DoSomething<T, U>(Expression<Func<T, U>> expr) where T : class
Niestety, parser uważa, że <T
to początek elementu HTML i kończy się błędem składni. Czy można utworzyć pomocnika z Razor, który jest metodą ogólną? Jeśli tak, jaka jest składnia?
asp.net-mvc
generics
asp.net-mvc-3
razor
mkedobbs
źródło
źródło
static
, chyba że zabraniają tego szczegóły implementacji; Powodem jest to, że można użyć ogólnych pomocników rozszerzeń :@helper Foo<T>(this T o) where T : IBar { }
Odpowiedzi:
Nie, obecnie nie jest to możliwe. Zamiast tego możesz napisać normalnego pomocnika HTML.
public static MvcHtmlString DoSomething<T, U>( this HtmlHelper htmlHelper, Expression<Func<T, U>> expr ) where T : class { ... }
i wtedy:
@(Html.DoSomething<SomeModel, string>(x => x.SomeProperty))
lub jeśli kierujesz model jako pierwszy argument ogólny:
public static MvcHtmlString DoSomething<TModel, TProperty>( this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expr ) where TModel : class { ... }
co pozwoli ci wywołać to w ten sposób (zakładając oczywiście, że twój widok jest silnie wpisany, ale jest to bezpieczne założenie, ponieważ wszystkie widoki i tak powinny być silnie wpisane :-)):
źródło
Jest to możliwe do osiągnięcia wewnątrz pliku pomocniczego ze
@functions
składnią, ale jeśli chcesz uzyskać czytelność w stylu maszynki do golenia, do której się odnosisz, będziesz musiał również wywołać zwykłego pomocnika, aby dopasować i zakończyć HTML.Zwróć uwagę, że funkcje w pliku pomocnika są statyczne, więc nadal musisz przekazać wystąpienie HtmlHelper ze strony, jeśli zamierzasz użyć jej metod.
np. Views \ MyView.cshtml:
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> } ...
źródło
System.Web.WebPages.Html.HtmlHelper
zamiastSystem.Web.Mvc.HtmlHelper
. Istnieje duża szansa, żeWebPages
wersja nie będzie dla Ciebie odpowiednia, ponieważ większość metod rozszerzających jest napisana przeciwkoSystem.Web.Mvc.HtmlHelper
. Ponadto nie maUrl
właściwości iUrlHelper
wymaga,RequestContext
co jest niedostępne w tejWebPages
wersji. W sumie prawdopodobnie będziesz musiał przejść wMvc
HtmlHelper
.{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` wApp_Code
.DoSomething
powinien być statyczny, abyś mógł wywołać@MyHelper.DoSomething(..)
swój widok. Jeśli ustawisz ją jako niestatyczną, musisz utworzyć instancjęMyHelper
first.We wszystkich przypadkach
TModel
będzie taki sam (model zadeklarowany dla widoku), aw moim przypadkuTValue
miał być taki sam, więc mogłem zadeklarować typ argumentu 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> }
Jeśli wszystkie pola modelu są dostępne
string
, możesz zastąpićMyClass
jestring
.Zdefiniowanie dwóch lub trzech pomocników ze
TValue
zdefiniowanym może nie być złe , ale jeśli masz więcej, które generują brzydki kod, nie znalazłem dobrego rozwiązania. Próbowałem zawinąć funkcję@helper
z funkcji, którą umieściłem wewnątrz@functions {}
bloku, ale nigdy nie udało mi się jej wykonać tej ścieżki.źródło
TModel
pewnie wiesz z góry.jeśli Twoim głównym problemem jest uzyskanie wartości atrybutu nazwy dla powiązania przy użyciu wyrażenia lambda
@Html.TextBoxFor(x => x.MyPoperty)
, a jeśli twój komponent ma bardzo złożone znaczniki HTML i powinien być zaimplementowany w pomocniku maszynki do golenia, to dlaczego nie utworzyć po prostu metody rozszerzenia wHtmlHelper<TModel>
celu rozwiązania problemu nazwa oprawy: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); } } }
Twój pomocnik maszynki do golenia powinien wyglądać jak zwykle:
@helper MyComponent(string name) { <input name="@name" type="text"/> }
to tutaj możesz go użyć
źródło
@Htm.IdFor
ale potrzebujesz dodatkowego procesu, aby przekonwertować go na string (.ToHtmlString()
), w którym pomocnik wymaga ciągu