Можно ли создать общий метод @helper с помощью Razor?

90

Я пытаюсь написать в Razor помощника, который выглядит следующим образом:

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

К сожалению, синтаксический анализатор считает, что <Tэто начало HTML-элемента, и я получаю синтаксическую ошибку. Можно ли с помощью Razor создать помощника, который является универсальным методом? Если да, то каков синтаксис?

mkedobbs
источник
По-прежнему не исправлено в текущей версии MVC 4. :(
Alex Dresko 03
1
Как это до сих пор не исправлено в VS2012?
Alex Dresko
7
Боже, не могу дождаться, когда это добавят; Я надеюсь, что это где-то около « реализовать вчера » в списке приоритетов. Частично не по теме, но вместе с этим я хотел бы видеть, что сгенерированные классы являются static, если это не запрещено деталями реализации; Причина в том, что можно использовать общие помощники по расширению :@helper Foo<T>(this T o) where T : IBar { }
Дэн Лагг,

Ответы:

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 TModel : class
{
    ...
}

что позволит вам вызывать его вот так (конечно, при условии, что ваше представление строго типизировано, но это безопасное предположение, потому что все представления в любом случае должны быть строго типизированными :-)):

@Html.DoSomething(x => x.SomeProperty)
Дарин Димитров
источник
10
Надеюсь, это то, что они добавят в будущую версию помощников Razor. Читаемость традиционного помощника намного ниже, чем синтаксиса @helper.
mkedobbs
2
Да согласен. Возврат к старому методу не только отстой, но и произвольно разбивает ваших помощников!
George R
127

Это является возможным достижение внутри хелперов файла с @functionsсинтаксисом , но если вы хотите читаемость бритва стиль вы имеете в виду вам необходимо будет также вызывать регулярные помощника , чтобы сделать HTML подгонки и отделки.

Обратите внимание, что функции в файле Helper статичны, поэтому вам все равно потребуется передать экземпляр 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>
}
...
Chrismilleruk
источник
Вам НЕ нужно делать метод статическим, и, следовательно, вам также НЕ нужно передавать свой Html / URL / модель и т. Д.
Sheepy
Хммм, почему у меня это не работает? Я получаю сообщение «Невозможно получить доступ к нестатическому методу TheThingToDo в статическом контексте» ..
TweeZz
12
@ Шипи, это правда только наполовину. Вы правы, вы можете сделать их нестатическими, но вы получите только System.Web.WebPages.Html.HtmlHelperвместо System.Web.Mvc.HtmlHelper. Велика вероятность, что эта WebPagesверсия вам не подойдет, поскольку большинство методов расширения написаны против System.Web.Mvc.HtmlHelper. Кроме того, здесь нет Urlсвойства, и UrlHelperтребуется, RequestContextчто он недоступен в WebPagesверсии. В общем, вам, вероятно, придется пройти в Mvc HtmlHelper.
Кирк Уолл,
1
помощник должен быть частью папки App_Code ??
Вишал Шарма
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
3

Во всех случаях TModelбудет одинаковым (модель, объявленная для представления), а в моем случае это TValueбудет то же самое, поэтому я смог объявить тип аргумента 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>
}

Если в вашей модели все поля string, вы можете заменить их MyClassна string.

Было бы неплохо определить двух или трех помощников с TValueопределенным, но если у вас есть еще какие-то, которые генерируют какой-то уродливый код, я действительно не нашел хорошего решения. Я попытался обернуть @helperиз функции, которую поместил в @functions {}блок, но так и не смог заставить ее работать по этому пути.

Bradlis7
источник
Кажется очевидным, если подумать - если TModelвы наверняка знаете это заранее.
ta.speot.is 02
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
Да, вы можете обойтись, @Htm.IdForно вам нужен дополнительный процесс для преобразования его в string ( .ToHtmlString()), где помощнику требуется строка
ktutnik