Создание пользовательского помощника Html: MyHelperFor

Я хотел бы создать помощник, который можно использовать как

@Html.MyHelperFor(m => m.Name)

это должно вернуться, например,

<span name="Name" data-something="Name"></span>

если это @Html.MyHelperFor(m => m.MailID) Это должно возвращать

<span name="MailID" data-something="MailID"></span>

Я должен иметь доступ к имени свойства в вспомогательном методе, чтобы сделать этот тип помощника, я думаю.

Как я могу это сделать?

Ответ 1

Вы можете сделать что-то вроде этого (следующие дополнительные атрибуты HTML тоже).

public static MvcHtmlString MyHelperFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression, object htmlAttributes = null)
{
    var data = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
    string propertyName = data.PropertyName;
    TagBuilder span = new TagBuilder("span");
    span.Attributes.Add("name", propertyName);
    span.Attributes.Add("data-something", "something");

    if (htmlAttributes != null)
    {
        var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
        span.MergeAttributes(attributes);
    }

    return new MvcHtmlString(span.ToString());
}

Ответ 2

Вы можете использовать метод FromLambaExpression из ModelMetadata следующим образом:

namespace System.Web.Mvc.Html
{
    public static class CustomHelpers
    {
        public static MvcHtmlString MyHelperFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression)
        {
            var metaData = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
            var name = metaData.PropertyName;
            // create your html string, you could defer to DisplayFor to render a span or
            // use the TagBuilder class to create a span and add your attributes to it
            string html = "";
            return new MvcHtmlString(html);
        }
    }
}

Класс ModelMetadata находится в пространстве имен System.Web.Mvc. Метод FromLambdaExpression используется встроенными помощниками, поэтому вы можете быть уверены, что ваш помощник будет функционировать так же, как встроенные помощники. Поместив класс CustomHelpers в пространство имен System.Web.Mvc.Html, вы можете получить доступ к своему помощнику, как к другим помощникам, т.е. @Html.MyHelperFor().

Ответ 3

Это должно помочь вам начать. Эта функция напрямую возвращает имя свойства, но вы должны иметь возможность преобразовать ее в расширение, которое вы ищете, с небольшой работой. В этом примере есть правильная подпись метода и вызов ExpressionHelper для получения имени вашего свойства.

    public static MvcHtmlString MyHelperFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression)
    {
        string expressionName = ExpressionHelper.GetExpressionText(expression);

        return new MvcHtmlString(expressionName);
    }

Ответ 4

Следя за ответом mattytommo, это отлично работает, но при использовании со сложными объектами существует только небольшая проблема, например, если вы используете этот код для свойства внутри EditorTemplate.

Вместо

var data = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
string propertyName = data.PropertyName;

Если вы используете MVC4, вы можете изменить его на

var propertyName = helper.NameFor(expression);

или для MVC3 и ниже

var propertyName = expression.Body.ToString();
propertyName = propertyName.Substring(propertyName.IndexOf(".") + 1);
if (!string.IsNullOrEmpty(helper.ViewData.TemplateInfo.HtmlFieldPrefix))
    propertyName = string.Format("{0}.{1}", helper.ViewData.TemplateInfo.HtmlFieldPrefix, propertyName);

Полный код:

    public static MvcHtmlString MyHelperFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression, object htmlAttributes = null)
    {
        var propertyName = expression.Body.ToString();
        propertyName = propertyName.Substring(propertyName.IndexOf(".") + 1);
        if (!string.IsNullOrEmpty(helper.ViewData.TemplateInfo.HtmlFieldPrefix))
            propertyName = string.Format("{0}.{1}", helper.ViewData.TemplateInfo.HtmlFieldPrefix, propertyName);

        TagBuilder span = new TagBuilder("span");
        span.Attributes.Add("name", propertyName);
        span.Attributes.Add("data-something", propertyName);

        if (htmlAttributes != null)
        {
            var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
            span.MergeAttributes(attributes);
        }

        return new MvcHtmlString(span.ToString());
    }