Чи можна створити загальний метод @helper за допомогою Razor?


89

Я намагаюся написати помічника в Razor, який виглядає так:

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

На жаль, синтаксичний аналізатор вважає, що <Tце початок елемента HTML, і в підсумку я маю синтаксичну помилку. Чи можна створити помічника за допомогою Razor, який є загальним методом? Якщо так, то який синтаксис?


Досі не виправлено у поточному випуску MVC 4. :(
Олексій Дресько 03.03.12

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

Це прекрасно. Дякую.
Ken Smith

Вам НЕ потрібно робити метод статичним, і, отже, вам також НЕ потрібно передавати свій Html / Url / Model тощо
Sheepy

12
@Sheepy, це правда лише наполовину. Ви праві, ви можете зробити їх нестатичними, але ви отримуєте лише, System.Web.WebPages.Html.HtmlHelperа не System.Web.Mvc.HtmlHelper. Існує чудова ймовірність того, що WebPagesверсія вам не підійде, оскільки більшість методів розширення написані проти System.Web.Mvc.HtmlHelper. Крім того, тут немає Urlвластивості, і вона UrlHelperпотрібна, RequestContextяка недоступна у WebPagesверсії. Загалом, вам, мабуть, доведеться здати Mvc HtmlHelper.
Kirk Woll

1
помічник повинен бути частиною папки App_Code ??
Vishal Sharma

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(..)у своєму поданні. Якщо ви зробите це нестатичним, вам потрібно буде створити екземпляр MyHelperfirst.
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 {}блоку, але ніколи не змусив її працювати за цим шляхом.


Очевидно, коли ви замислюєтесь над цим - якщо це TModelви, напевно, знаєте це заздалегідь.
ta.speot.is 02.03.17

0

якщо ваша основна проблема полягає в отриманні значення атрибута name для прив'язки за допомогою лямбда-виразу, здається @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 (...)?
Олексій Дресько,

Так, ви можете зробити, @Htm.IdForале вам потрібен додатковий процес, щоб перетворити його на string ( .ToHtmlString()), де помічнику потрібен рядок
ktutnik
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.